From c7026b90940a1d88f9c10a6d98263bf22e654fa5 Mon Sep 17 00:00:00 2001 From: Adubbz Date: Sun, 8 Mar 2020 19:06:23 +1100 Subject: [PATCH] Implement the NCM sysmodule (closes #91) * Implement NCM * Modernize ncm_main * Remove unnecessary smExit * Give access to svcCallSecureMonitor * Stack size bump * Fix incorrect setup for NandUser's content storage entry * Fix a potential data abort when flushing the placeholder accessor cache * Fix HasFile and HasDirectory * Use r+b, not w+b * Misc fixes * errno begone * Fixed more stdio error handling * More main fixes * Various command improvements * Make dispatch tables great again * Fix logic inversion * Fixed content path generation * Bump heap size, fix CleanupAllPlaceHolder * Various fixes. Note: This contains debug stuff which will be removed later. I was getting tired of having to cherrypick tiny changes * Fixed placeholder/content deletion * Fixed incorrect content manager destruction * Prevent automatic placeholder creation on open * Fixed List implementation. Also lots of debug logging. * Removed debug code * Added a scope guard for WritePlaceHolder * Manually prevent placeholder/content appending * Revert "Removed debug code" This reverts commit d6ff261fcc8c1f26968e894b02c17a01a12ec98b. * Always cache placeholder file. Switch to ftell for preventing appending * Universally use EnsureEnabled * Abstract away file writing logic * Misc cleanup * Refactor placeholder cacheing * Remove debug code (again) * Revert "Remove debug code (again)" This reverts commit 168447d80e9640768fb1b43f04a385507c1bb5ab. * Misc changes * Fixed file modes * Fixed ContentId/PlaceHolderId alignment * Improved type safety * Fixed reinitialization * Fixed doubleup on path creation * Remove debug code * Fixed 1.0.0 booting * Correct amount of add on content * Correct main thread stack size * lr: Introducing registered data * Reorder stratosphere Makefile * Move results to libstrat * lr: Cleanup lr_redirection * lr: lr_manager tweaks * lr: Imrpoved path handling and adjust ResolveAddOnContentPath order * lr: Organise types * Add eof newlines * lr: Eliminate unnecessary vars * lr: Unnecessary vars 2 electric boogaloo * lr: Various helpers * lr: RegisteredLocationResolver helpers * ncm: Move ncm_types to libstrat * ncm: Misc cleanup * Implement NCM * Modernize ncm_main * Remove unnecessary smExit * Give access to svcCallSecureMonitor * Stack size bump * Fix incorrect setup for NandUser's content storage entry * Fix a potential data abort when flushing the placeholder accessor cache * Fix HasFile and HasDirectory * Use r+b, not w+b * Misc fixes * errno begone * Fixed more stdio error handling * More main fixes * Various command improvements * Make dispatch tables great again * Fix logic inversion * Fixed content path generation * Bump heap size, fix CleanupAllPlaceHolder * Various fixes. Note: This contains debug stuff which will be removed later. I was getting tired of having to cherrypick tiny changes * Fixed placeholder/content deletion * Fixed incorrect content manager destruction * Prevent automatic placeholder creation on open * Fixed List implementation. Also lots of debug logging. * Removed debug code * Added a scope guard for WritePlaceHolder * Manually prevent placeholder/content appending * Revert "Removed debug code" This reverts commit d6ff261fcc8c1f26968e894b02c17a01a12ec98b. * Always cache placeholder file. Switch to ftell for preventing appending * Universally use EnsureEnabled * Abstract away file writing logic * Misc cleanup * Refactor placeholder cacheing * Remove debug code (again) * Revert "Remove debug code (again)" This reverts commit 168447d80e9640768fb1b43f04a385507c1bb5ab. * Misc changes * Fixed file modes * Fixed ContentId/PlaceHolderId alignment * Improved type safety * Fixed reinitialization * Fixed doubleup on path creation * Remove debug code * Fixed 1.0.0 booting * Correct amount of add on content * Correct main thread stack size * lr: Introducing registered data * Reorder stratosphere Makefile * Move results to libstrat * lr: Cleanup lr_redirection * lr: lr_manager tweaks * lr: Imrpoved path handling and adjust ResolveAddOnContentPath order * lr: Organise types * Add eof newlines * lr: Eliminate unnecessary vars * lr: Unnecessary vars 2 electric boogaloo * lr: Various helpers * lr: RegisteredLocationResolver helpers * ncm: Move ncm_types to libstrat * ncm: Misc cleanup * Updated AddOnContentLocationResolver and RegisteredLocationResolver to 9.0.0 * Finished updating lr to 9.0.0 * Updated NCM to 9.0.0 * Fix libstrat includes * Fixed application launching * title_id_2 -> owner_tid * Updated to new-ipc * Change to using pure virtuals * Title Id -> Program Id * Fixed compilation against master * std::scoped_lock<> -> std::scoped_lock * Adopted R_UNLESS and R_CONVERT * Prefix namespace to Results * Adopt std::numeric_limits * Fixed incorrect error handling in ReadFile * Adopted AMS_ABORT_UNLESS * Adopt util::GenerateUuid() * Syntax improvements * ncm_types: Address review * Address more review comments * Updated copyrights * Address more feedback * More feedback addressed * More changes * Move dispatch tables out of interface files * Addressed remaining comments * lr: move into libstratosphere * ncm: Fix logic inversion * lr: Add comments * lr: Remove whitespace * ncm: Start addressing feedback * ncm: Cleanup InitializeContentManager * lr: support client-side usage * lr_service -> lr_api * ncm: Begin refactoring content manager * ncm: More content manager improvements * ncm: Content manager mount improvements * ldr: use lr bindings * lr bindings usage: minor fixes * ncm/lr: Pointer placement * ncm: placeholder accessor cleanup * ncm: minor fixes * ncm: refactor rights cache * ncm: content meta database cleanup * ncm: move content meta database impl out of interface file * ncm: Use const ContentMetaKey & * ncm: fix other non-const ContentMetaKey references * ncm: content meta database cleanup * ncm: content storage fixes for 2.0.0 * ncm: add missing end of file newlines * ncm: implement ContentMetaReader * ncm: client-side api * ncm: trim trailing spaces * ncm: FS_MAX_PATH-1 -> fs::EntryNameLengthMax * ncm: Use PathString and Path * fs: implement accessor wrappers for ncm * fs: implement user fs wrappers * fs: add MountSdCard * ncm: move to content manager impl * ncm: fix up main * kvdb: use fs:: * fs: Add wrappers needed for ncm * ncm: use fs bindings, other refactoring * ncm: minor fixes * fsa: fix ReadFile without size output * fs: add substorage, rom path tool * ncm: fix dangling fsdev usage * fs: fix bug in Commit * fs: fixed incorrect mode check * fs: implement Mount(System)Data * ncm: don't delete hos * results: add R_SUCCEED_IF * ams-except-ncm: use R_SUCCEED_IF * ncm: added comments * ncm: fix api definitions * ncm: use R_SUCCEED_IF * pm: think of the savings * ncm: employ kernel strats * ncm: Nintendo has 5 MiB of heap. Give ourselves 4 to be safe, pending analysis * ncm: refactor IDs, split types header into many headers * ams.mitm: use fs bindings instead of stdio * fs: SystemData uses SystemDataId * ncm: improve meta-db accuracy * ncm: inline getlatestkey * fs: improve UnsupportedOperation results * fs: modernize mount utils * ams: misc fixes for merge-errors * fs: improve unsupportedoperation results * git subrepo pull emummc subrepo: subdir: "emummc" merged: "d12dd546" upstream: origin: "https://github.com/m4xw/emuMMC" branch: "develop" commit: "d12dd546" git-subrepo: version: "0.4.1" origin: "???" commit: "???" * util: add boundedmap * ncm: minor style fixes * ncm: don't unmount if mounting fails * lr: bug fixes * ncm: implement ncm.for-initialize + ncm.for-safemode * lr: ncm::ProgramId::Invalid -> ncm::InvalidProgramId * ncm: fix open directory mode on 1.0.0 * ncm: fix fs use, implement more of < 4.0.0 for-initialize/safemode * ncm: implement packagedcontent -> content for building metadb * ncm: fix save data flag management * ncm: address some review suggestions (thanks @leoetlino!) * updater: use fs bindings * fs: implement MountCode * fs: prefer make_unique to operator new * ncm: implement remaining ContentMetaDatabaseBuilder functionality Co-authored-by: Michael Scire --- emummc/.gitrepo | 6 +- emummc/README.md | 14 +- emummc/source/FS/offsets/100.h | 6 +- emummc/source/main.c | 22 +- .../arch/arm64/kern_k_interrupt_manager.cpp | 8 +- .../source/arch/arm64/kern_k_page_table.cpp | 12 +- .../nintendo/nx/kern_k_device_page_table.cpp | 2 +- .../source/kern_k_capabilities.cpp | 4 +- .../source/kern_k_page_group.cpp | 4 +- .../libstratosphere/include/stratosphere.hpp | 6 +- .../include/stratosphere/cfg/cfg_types.hpp | 4 +- .../stratosphere/dmnt/dmnt_cheat_types.hpp | 6 +- .../stratosphere/fatal/fatal_types.hpp | 4 +- .../include/stratosphere/fs.hpp | 20 + .../include/stratosphere/fs/fs_bis.hpp | 58 ++ .../include/stratosphere/fs/fs_code.hpp | 24 + .../include/stratosphere/fs/fs_common.hpp | 33 +- .../include/stratosphere/fs/fs_content.hpp | 34 + .../stratosphere/fs/fs_content_storage.hpp | 34 + .../fs/fs_dbm_hierarchical_rom_file_table.hpp | 200 +++++ .../fs/fs_dbm_rom_key_value_storage.hpp | 380 +++++++++ .../stratosphere/fs/fs_dbm_rom_path_tool.hpp | 122 +++ .../stratosphere/fs/fs_dbm_rom_types.hpp | 59 ++ .../include/stratosphere/fs/fs_directory.hpp | 8 + .../include/stratosphere/fs/fs_file.hpp | 15 + .../include/stratosphere/fs/fs_filesystem.hpp | 29 + .../include/stratosphere/fs/fs_game_card.hpp | 47 ++ .../include/stratosphere/fs/fs_istorage.hpp | 28 +- .../stratosphere/fs/fs_memory_management.hpp | 51 ++ .../stratosphere/fs/fs_memory_storage.hpp | 84 ++ .../include/stratosphere/fs/fs_mount.hpp | 27 + .../include/stratosphere/fs/fs_path_tool.hpp | 13 + .../stratosphere/fs/fs_query_range.hpp | 3 + .../stratosphere/fs/fs_remote_filesystem.hpp | 170 ++-- .../stratosphere/fs/fs_remote_storage.hpp | 12 +- .../include/stratosphere/fs/fs_rights_id.hpp | 32 + .../stratosphere/fs/fs_romfs_filesystem.hpp | 74 ++ .../fs/fs_save_data_management.hpp | 29 + .../fs/fs_save_data_transaction.hpp | 24 + .../stratosphere/fs/fs_save_data_types.hpp | 159 ++++ .../include/stratosphere/fs/fs_sd_card.hpp | 23 + .../fs/fs_signed_system_partition.hpp | 24 + .../include/stratosphere/fs/fs_substorage.hpp | 144 ++++ .../stratosphere/fs/fs_system_data.hpp | 26 + .../stratosphere/fs/fs_system_save_data.hpp | 40 + .../include/stratosphere/fs/fsa/fs_ifile.hpp | 4 +- .../stratosphere/fs/fsa/fs_ifilesystem.hpp | 11 +- .../stratosphere/fs/fsa/fs_registrar.hpp | 35 + .../fs/impl/fs_common_mount_name.hpp | 50 ++ .../include/stratosphere/fs/impl/fs_data.hpp | 27 + .../fs/impl/fs_filesystem_proxy_type.hpp | 33 + .../stratosphere/fs/impl/fs_newable.hpp | 40 + .../stratosphere/fssrv/fssrv_sf_path.hpp | 6 +- .../stratosphere/kvdb/kvdb_archive.hpp | 3 +- .../stratosphere/kvdb/kvdb_auto_buffer.hpp | 8 +- .../stratosphere/kvdb/kvdb_bounded_string.hpp | 3 +- .../kvdb/kvdb_file_key_value_cache.hpp | 75 +- .../kvdb/kvdb_file_key_value_store.hpp | 4 +- .../kvdb/kvdb_memory_key_value_store.hpp | 77 +- .../include/stratosphere/ldr/ldr_pm_api.hpp | 2 +- .../include/stratosphere/ldr/ldr_types.hpp | 5 +- .../include/stratosphere/lr.hpp | 21 + .../lr_add_on_content_location_resolver.hpp | 75 ++ .../include/stratosphere/lr/lr_api.hpp | 35 + .../lr_i_add_on_content_location_resolver.hpp | 52 ++ .../lr/lr_i_location_resolver.hpp | 115 +++ .../lr/lr_i_location_resolver_manager.hpp | 41 + .../lr/lr_i_registered_location_resolver.hpp | 75 ++ .../stratosphere/lr/lr_location_resolver.hpp | 175 ++++ .../lr/lr_location_resolver_manager_impl.hpp | 47 ++ .../lr/lr_registered_location_resolver.hpp | 113 +++ .../include/stratosphere/lr/lr_types.hpp | 61 ++ .../include/stratosphere/ncm.hpp | 12 +- .../include/stratosphere/ncm/ncm_api.hpp | 53 ++ .../stratosphere/ncm/ncm_auto_buffer.hpp | 88 ++ .../stratosphere/ncm/ncm_bounded_map.hpp | 24 + .../stratosphere/ncm/ncm_content_id.hpp | 45 ++ .../stratosphere/ncm/ncm_content_id_utils.hpp | 38 + .../stratosphere/ncm/ncm_content_info.hpp | 61 ++ .../ncm/ncm_content_info_data.hpp | 45 ++ .../ncm/ncm_content_management_utils.hpp | 39 + .../ncm/ncm_content_manager_config.hpp | 38 + .../ncm/ncm_content_manager_impl.hpp | 121 +++ .../stratosphere/ncm/ncm_content_meta.hpp | 315 ++++++++ .../ncm/ncm_content_meta_database.hpp | 194 +++++ .../stratosphere/ncm/ncm_content_meta_id.hpp | 78 ++ .../stratosphere/ncm/ncm_content_meta_key.hpp | 114 +++ .../ncm/ncm_content_meta_type.hpp | 36 + .../ncm/ncm_content_meta_utils.hpp | 27 + .../stratosphere/ncm/ncm_content_storage.hpp | 200 +++++ .../stratosphere/ncm/ncm_content_type.hpp | 31 + .../include/stratosphere/ncm/ncm_data_id.hpp | 54 ++ .../ncm/ncm_i_content_manager.hpp | 74 ++ .../ncm/ncm_i_content_meta_database.hpp | 96 +++ .../ncm/ncm_i_content_storage.hpp | 130 +++ .../include/stratosphere/ncm/ncm_ids.hpp | 20 + .../stratosphere/ncm/ncm_make_path.hpp | 38 + .../include/stratosphere/ncm/ncm_path.hpp | 39 + .../stratosphere/ncm/ncm_path_string.hpp | 24 + .../stratosphere/ncm/ncm_placeholder_id.hpp | 45 ++ .../stratosphere/ncm/ncm_program_id.hpp | 59 ++ .../stratosphere/ncm/ncm_program_location.hpp | 34 + .../stratosphere/ncm/ncm_rights_id.hpp | 30 + .../stratosphere/ncm/ncm_rights_id_cache.hpp | 90 +++ .../stratosphere/ncm/ncm_storage_id.hpp | 39 + .../ncm/ncm_system_content_meta_id.hpp | 509 ++++++++++++ .../include/stratosphere/ncm/ncm_types.hpp | 447 ---------- .../include/stratosphere/os/os_condvar.hpp | 4 +- .../include/stratosphere/os/os_mutex.hpp | 12 +- .../include/stratosphere/pm/pm_dmnt_api.hpp | 5 +- .../include/stratosphere/pm/pm_info_api.hpp | 7 +- .../include/stratosphere/pm/pm_shell_api.hpp | 5 +- .../include/stratosphere/ro/ro_types.hpp | 2 +- .../sf/impl/sf_impl_command_serialization.hpp | 2 + .../stratosphere/sm/sm_manager_api.hpp | 8 +- .../stratosphere/sm/sm_scoped_holder.hpp | 7 +- .../include/stratosphere/sm/sm_types.hpp | 4 +- .../include/stratosphere/spl/spl_types.hpp | 2 +- .../source/boot2/boot2_api.cpp | 212 ++--- .../source/cfg/cfg_override.cpp | 6 +- .../libstratosphere/source/fs/fs_bis.cpp | 127 +++ .../libstratosphere/source/fs/fs_code.cpp | 44 + .../libstratosphere/source/fs/fs_content.cpp | 74 ++ .../source/fs/fs_content_storage.cpp | 93 +++ .../libstratosphere/source/fs/fs_data.cpp | 89 ++ .../fs/fs_dbm_hierarchical_rom_file_table.cpp | 578 +++++++++++++ .../source/fs/fs_dbm_rom_path_tool.cpp | 195 +++++ .../source/fs/fs_file_path_hash.hpp | 36 + .../source/fs/fs_file_storage.cpp | 8 +- .../source/fs/fs_game_card.cpp | 87 ++ .../source/fs/fs_memory_management.cpp | 83 ++ .../source/fs/fs_path_utils.cpp | 2 +- .../source/fs/fs_rights_id.cpp | 31 + .../source/fs/fs_romfs_filesystem.cpp | 536 ++++++++++++ .../source/fs/fs_save_data_management.cpp | 119 +++ .../source/fs/fs_scoped_setter.hpp | 60 ++ .../libstratosphere/source/fs/fs_sd_card.cpp | 37 + .../source/fs/fs_signed_system_partition.cpp | 48 ++ .../source/fs/fs_system_data.cpp | 33 + .../source/fs/fs_system_save_data.cpp | 61 ++ .../source/fs/fsa/fs_directory_accessor.cpp | 39 + .../source/fs/fsa/fs_directory_accessor.hpp | 38 + .../source/fs/fsa/fs_file_accessor.cpp | 123 +++ .../source/fs/fsa/fs_file_accessor.hpp | 68 ++ .../source/fs/fsa/fs_filesystem_accessor.cpp | 243 ++++++ .../source/fs/fsa/fs_filesystem_accessor.hpp | 94 +++ .../source/fs/fsa/fs_mount_name.hpp | 26 + .../source/fs/fsa/fs_mount_table.cpp | 75 ++ .../source/fs/fsa/fs_mount_table.hpp | 40 + .../source/fs/fsa/fs_mount_utils.cpp | 168 ++++ .../source/fs/fsa/fs_mount_utils.hpp | 30 + .../source/fs/fsa/fs_registrar.cpp | 51 ++ .../source/fs/fsa/fs_user_directory.cpp | 42 + .../source/fs/fsa/fs_user_file.cpp | 77 ++ .../source/fs/fsa/fs_user_filesystem.cpp | 199 +++++ .../source/fs/fsa/fs_user_mount_table.cpp | 41 + .../source/fs/fsa/fs_user_mount_table.hpp | 27 + ...fssystem_directory_savedata_filesystem.cpp | 2 +- .../libstratosphere/source/hid/hid_api.cpp | 2 +- .../source/kvdb/kvdb_file_key_value_store.cpp | 74 +- ..._add_on_content_location_resolver_impl.cpp | 80 ++ ..._add_on_content_location_resolver_impl.hpp | 40 + .../libstratosphere/source/lr/lr_api.cpp | 66 ++ .../lr/lr_content_location_resolver_impl.cpp | 197 +++++ .../lr/lr_content_location_resolver_impl.hpp | 66 ++ .../source/lr/lr_location_redirector.cpp | 127 +++ .../source/lr/lr_location_redirector.hpp | 60 ++ .../lr/lr_location_resolver_impl_base.hpp | 54 ++ .../lr/lr_location_resolver_manager_impl.cpp | 90 +++ ...r_redirect_only_location_resolver_impl.cpp | 160 ++++ ...r_redirect_only_location_resolver_impl.hpp | 55 ++ .../source/lr/lr_registered_data.hpp | 145 ++++ .../lr_registered_location_resolver_impl.cpp | 150 ++++ .../lr_registered_location_resolver_impl.hpp | 68 ++ .../lr/lr_remote_location_resolver_impl.hpp | 148 ++++ ...mote_registered_location_resolver_impl.hpp | 101 +++ .../libstratosphere/source/ncm/ncm_api.cpp | 112 +++ .../source/ncm/ncm_content_id_utils.cpp | 72 ++ .../ncm/ncm_content_management_utils.cpp | 209 +++++ .../source/ncm/ncm_content_manager_impl.cpp | 663 +++++++++++++++ .../source/ncm/ncm_content_meta.cpp | 101 +++ .../ncm/ncm_content_meta_database_impl.cpp | 443 ++++++++++ .../ncm/ncm_content_meta_database_impl.hpp | 54 ++ .../ncm_content_meta_database_impl_base.hpp | 57 ++ .../source/ncm/ncm_content_meta_type.cpp | 29 + .../source/ncm/ncm_content_meta_utils.cpp | 80 ++ .../source/ncm/ncm_content_storage_impl.cpp | 760 ++++++++++++++++++ .../source/ncm/ncm_content_storage_impl.hpp | 76 ++ .../ncm/ncm_content_storage_impl_base.hpp | 48 ++ .../source/ncm/ncm_fs_utils.cpp | 158 ++++ .../source/ncm/ncm_fs_utils.hpp | 50 ++ .../source/ncm/ncm_make_path.cpp | 151 ++++ ...m_on_memory_content_meta_database_impl.cpp | 99 +++ ...m_on_memory_content_meta_database_impl.hpp | 33 + .../source/ncm/ncm_placeholder_accessor.cpp | 256 ++++++ .../source/ncm/ncm_placeholder_accessor.hpp | 79 ++ .../ncm_read_only_content_storage_impl.cpp | 264 ++++++ .../ncm_read_only_content_storage_impl.hpp | 59 ++ .../ncm/ncm_remote_content_manager_impl.hpp | 94 +++ .../ncm_remote_content_meta_database_impl.hpp | 161 ++++ .../ncm/ncm_remote_content_storage_impl.hpp | 194 +++++ .../source/updater/updater_api.cpp | 163 ++-- .../source/updater/updater_bis_management.cpp | 43 +- .../source/updater/updater_bis_management.hpp | 42 +- .../source/updater/updater_files.cpp | 52 +- .../source/updater/updater_paths.cpp | 13 +- .../source/updater/updater_paths.hpp | 2 +- .../include/vapours/results/fs_results.hpp | 178 +++- .../include/vapours/results/lr_results.hpp | 1 + .../include/vapours/results/ncm_results.hpp | 27 +- .../vapours/results/results_common.hpp | 3 + libraries/libvapours/include/vapours/util.hpp | 1 + .../include/vapours/util/util_bounded_map.hpp | 129 +++ .../vapours/util/util_intrusive_list.hpp | 4 +- stratosphere/Makefile | 2 +- stratosphere/ams_mitm/source/amsmitm_main.cpp | 2 +- .../source/bpc_mitm/bpc_mitm_service.hpp | 4 +- .../source/fs_mitm/fs_mitm_service.cpp | 13 +- .../source/fs_mitm/fs_mitm_service.hpp | 8 +- .../source/fs_mitm/fsmitm_boot0storage.cpp | 2 +- .../source/fs_mitm/fsmitm_boot0storage.hpp | 3 +- .../fs_mitm/fsmitm_layered_romfs_storage.cpp | 2 +- .../fs_mitm/fsmitm_layered_romfs_storage.hpp | 10 + .../source/ns_mitm/ns_am_mitm_service.hpp | 2 +- .../source/ns_mitm/ns_web_mitm_service.hpp | 2 +- .../source/set_mitm/set_mitm_service.cpp | 4 +- .../source/set_mitm/set_mitm_service.hpp | 4 +- .../source/set_mitm/setsys_mitm_service.cpp | 14 +- stratosphere/boot/source/boot_i2c_utils.cpp | 2 +- stratosphere/boot/source/boot_main.cpp | 3 +- .../i2c/driver/impl/i2c_bus_accessor.cpp | 6 +- stratosphere/boot2/source/boot2_main.cpp | 8 +- stratosphere/creport/source/creport_main.cpp | 2 +- .../dmnt/source/cheat/impl/dmnt_cheat_api.cpp | 2 +- stratosphere/dmnt/source/dmnt_main.cpp | 6 +- .../dmnt/source/dmnt_service_target_io.cpp | 2 +- stratosphere/eclct.stub/source/eclct_stub.cpp | 2 +- stratosphere/fatal/source/fatal_main.cpp | 2 +- stratosphere/fatal/source/fatal_service.cpp | 6 +- .../source/ldr_anti_downgrade_tables.inc | 278 +++---- .../loader/source/ldr_content_management.cpp | 48 +- stratosphere/loader/source/ldr_main.cpp | 6 +- .../loader/source/ldr_process_creation.cpp | 5 +- stratosphere/ncm/Makefile | 122 +++ stratosphere/ncm/ncm.json | 67 ++ stratosphere/ncm/source/ncm_main.cpp | 231 ++++++ .../pm/source/impl/pm_process_manager.cpp | 6 +- stratosphere/pm/source/pm_main.cpp | 4 +- .../ro/source/impl/ro_service_impl.cpp | 2 +- stratosphere/ro/source/ro_main.cpp | 2 +- .../sm/source/impl/sm_service_manager.cpp | 13 +- stratosphere/sm/source/sm_main.cpp | 2 +- stratosphere/sm/source/sm_manager_service.cpp | 2 +- stratosphere/spl/source/spl_main.cpp | 2 +- 254 files changed, 16876 insertions(+), 1274 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_content.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_mount.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_romfs_filesystem.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_save_data_transaction.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_signed_system_partition.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_system_data.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/impl/fs_data.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/impl/fs_filesystem_proxy_type.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_add_on_content_location_resolver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_i_add_on_content_location_resolver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_i_registered_location_resolver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_registered_location_resolver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_bounded_map.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_management_utils.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_type.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_type.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_data_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_ids.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_make_path.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_path_string.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp delete mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp create mode 100644 libraries/libstratosphere/source/fs/fs_bis.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_code.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_content.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_content_storage.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_data.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_dbm_hierarchical_rom_file_table.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_dbm_rom_path_tool.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_file_path_hash.hpp create mode 100644 libraries/libstratosphere/source/fs/fs_game_card.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_memory_management.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_rights_id.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_romfs_filesystem.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_save_data_management.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_scoped_setter.hpp create mode 100644 libraries/libstratosphere/source/fs/fs_sd_card.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_signed_system_partition.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_system_data.cpp create mode 100644 libraries/libstratosphere/source/fs/fs_system_save_data.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.hpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_file_accessor.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_file_accessor.hpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_mount_table.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_registrar.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_user_directory.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_user_file.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.cpp create mode 100644 libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.cpp create mode 100644 libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_api.cpp create mode 100644 libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.cpp create mode 100644 libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_location_redirector.cpp create mode 100644 libraries/libstratosphere/source/lr/lr_location_redirector.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_location_resolver_impl_base.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_location_resolver_manager_impl.cpp create mode 100644 libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.cpp create mode 100644 libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_registered_data.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.cpp create mode 100644 libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp create mode 100644 libraries/libstratosphere/source/lr/lr_remote_registered_location_resolver_impl.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_api.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_id_utils.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_meta.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_meta_type.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_storage_impl_base.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_make_path.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_remote_content_storage_impl.hpp create mode 100644 libraries/libvapours/include/vapours/util/util_bounded_map.hpp create mode 100644 stratosphere/ncm/Makefile create mode 100644 stratosphere/ncm/ncm.json create mode 100644 stratosphere/ncm/source/ncm_main.cpp diff --git a/emummc/.gitrepo b/emummc/.gitrepo index 3336bd207..a6da23bb7 100644 --- a/emummc/.gitrepo +++ b/emummc/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/m4xw/emuMMC branch = develop - commit = bd81a674a946c30b566e1732a95c18f19b701558 - parent = 6ee525201ccef107c61d81ba73c891e3eb5f0215 + commit = d12dd5464422029a1e5601916517ec3f1c81d8d0 + parent = 259a1a7513236a1de4d373bc6cb99032ede2c626 method = rebase - cmdver = 0.4.0 + cmdver = 0.4.1 diff --git a/emummc/README.md b/emummc/README.md index 604292ddc..a4dd40a55 100644 --- a/emummc/README.md +++ b/emummc/README.md @@ -1,21 +1,21 @@ # emuMMC -*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw*** +*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw*** ### Supported Horizon Versions **1.0.0 - 9.1.0** ## Features -* Arbitrary SDMMC backend selection +* Arbitrary SDMMC backend selection **This allows loading eMMC from SD or even SD from eMMC** -* On the fly hooking / patching, fully self-infesting +* On the fly hooking / patching, fully self-infesting **Only one payload required for all versions!** -* File-based SDMMC backend support (from SD) +* File-based SDMMC backend support (from SD) **This allows loading eMMC images from hekate-backups (split or not)** -* SDMMC device based sector offset (*currently eMMC only*) +* SDMMC device based sector offset (*currently eMMC only*) **Raw partition support for eMMC from SD with less performance overhead** -* Full support for `/Nintendo` folder redirection to a arbitrary path +* Full support for `/Nintendo` folder redirection to a arbitrary path **No 8 char length restriction!** -* exosphere based context configuration +* exosphere based context configuration **This includes full support for multiple emuMMC images** ## Compiling diff --git a/emummc/source/FS/offsets/100.h b/emummc/source/FS/offsets/100.h index 7b4e6fc01..779601580 100644 --- a/emummc/source/FS/offsets/100.h +++ b/emummc/source/FS/offsets/100.h @@ -48,10 +48,8 @@ // Nintendo Paths #define FS_OFFSET_100_NINTENDO_PATHS \ { \ - {.opcode_reg = 9, .adrp_offset = 0x00032C58, .add_rel_offset = 4}, \ - {.opcode_reg = 8, .adrp_offset = 0x00032C60, .add_rel_offset = 4}, \ - {.opcode_reg = 9, .adrp_offset = 0x00032F3C, .add_rel_offset = 4}, \ - {.opcode_reg = 8, .adrp_offset = 0x00032F44, .add_rel_offset = 4}, \ + {.opcode_reg = 8, .adrp_offset = 0x00032C58, .add_rel_offset = 8}, \ + {.opcode_reg = 9, .adrp_offset = 0x00032F40, .add_rel_offset = 8}, \ {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ } diff --git a/emummc/source/main.c b/emummc/source/main.c index efd7ac049..520c1ea22 100644 --- a/emummc/source/main.c +++ b/emummc/source/main.c @@ -48,6 +48,9 @@ extern char __argdata__; // TODO static char nintendo_path[0x80] = "Nintendo"; +// 1.0.0 requires special path handling because it has separate album and contents paths. +#define FS_100_ALBUM_PATH 0 +#define FS_100_CONTENTS_PATH 1 static char nintendo_path_album_100[0x100] = "/Nintendo/Album"; static char nintendo_path_contents_100[0x100] = "/Nintendo/Contents"; @@ -275,23 +278,18 @@ void setup_nintendo_paths(void) // 1.0.0 needs special handling because it uses two paths. // Do album path { - int path_len = snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path); + snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path); intptr_t nintendo_album_path_location = (intptr_t)&nintendo_path_album_100; - intptr_t album_path_location = nintendo_album_path_location + path_len - 6; // "/Album" - uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[0].adrp_offset); - uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[1].adrp_offset); - write_adrp_add(fs_offsets->nintendo_paths[0].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[0].add_rel_offset, nintendo_album_path_location); - write_adrp_add(fs_offsets->nintendo_paths[1].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[1].add_rel_offset, album_path_location); + uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].adrp_offset); + write_adrp_add(fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].add_rel_offset, nintendo_album_path_location); } + // Do contents path { - int path_len = snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path); + snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path); intptr_t nintendo_contents_path_location = (intptr_t)&nintendo_path_contents_100; - intptr_t contents_path_location = nintendo_contents_path_location + path_len - 9; // "/Contents" - uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[2].adrp_offset); - uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[3].adrp_offset); - write_adrp_add(fs_offsets->nintendo_paths[2].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[2].add_rel_offset, nintendo_contents_path_location); - write_adrp_add(fs_offsets->nintendo_paths[3].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[3].add_rel_offset, contents_path_location); + uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].adrp_offset); + write_adrp_add(fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].add_rel_offset, nintendo_contents_path_location); } } } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp index 3b866be8e..78711d0e1 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp @@ -262,8 +262,8 @@ namespace ams::kern::arch::arm64 { R_UNLESS(entry.handler != nullptr, svc::ResultInvalidState()); /* If auto-cleared, we can succeed immediately. */ - R_UNLESS(entry.manually_cleared, ResultSuccess()); - R_UNLESS(entry.needs_clear, ResultSuccess()); + R_SUCCEED_IF(!entry.manually_cleared); + R_SUCCEED_IF(!entry.needs_clear); /* Clear and enable. */ entry.needs_clear = false; @@ -277,8 +277,8 @@ namespace ams::kern::arch::arm64 { R_UNLESS(entry.handler != nullptr, svc::ResultInvalidState()); /* If auto-cleared, we can succeed immediately. */ - R_UNLESS(entry.manually_cleared, ResultSuccess()); - R_UNLESS(entry.needs_clear, ResultSuccess()); + R_SUCCEED_IF(!entry.manually_cleared); + R_SUCCEED_IF(!entry.needs_clear); /* Clear and set priority. */ entry.needs_clear = false; diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp index db1c1e27b..9429ef854 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -832,7 +832,7 @@ namespace ams::kern::arch::arm64 { L1PageTableEntry *l1_entry = impl.GetL1Entry(virt_addr); if (l1_entry->IsBlock()) { /* If our block size is too big, don't bother. */ - R_UNLESS(block_size < L1BlockSize, ResultSuccess()); + R_SUCCEED_IF(block_size >= L1BlockSize); /* Get the addresses we're working with. */ const KProcessAddress block_virt_addr = util::AlignDown(GetInteger(virt_addr), L1BlockSize); @@ -859,10 +859,10 @@ namespace ams::kern::arch::arm64 { } /* If we don't have an l1 table, we're done. */ - R_UNLESS(l1_entry->IsTable(), ResultSuccess()); + R_SUCCEED_IF(!l1_entry->IsTable()); /* We want to separate L2 contiguous blocks into L2 blocks, so check that our size permits that. */ - R_UNLESS(block_size < L2ContiguousBlockSize, ResultSuccess()); + R_SUCCEED_IF(block_size >= L2ContiguousBlockSize); L2PageTableEntry *l2_entry = impl.GetL2Entry(l1_entry, virt_addr); if (l2_entry->IsBlock()) { @@ -878,7 +878,7 @@ namespace ams::kern::arch::arm64 { } /* We want to separate L2 blocks into L3 contiguous blocks, so check that our size permits that. */ - R_UNLESS(block_size < L2BlockSize, ResultSuccess()); + R_SUCCEED_IF(block_size >= L2BlockSize); /* Get the addresses we're working with. */ const KProcessAddress block_virt_addr = util::AlignDown(GetInteger(virt_addr), L2BlockSize); @@ -905,10 +905,10 @@ namespace ams::kern::arch::arm64 { } /* If we don't have an L3 table, we're done. */ - R_UNLESS(l2_entry->IsTable(), ResultSuccess()); + R_SUCCEED_IF(!l2_entry->IsTable()); /* We want to separate L3 contiguous blocks into L2 blocks, so check that our size permits that. */ - R_UNLESS(block_size < L3ContiguousBlockSize, ResultSuccess()); + R_SUCCEED_IF(block_size >= L3ContiguousBlockSize); /* If we're contiguous, try to separate. */ L3PageTableEntry *l3_entry = impl.GetL3Entry(l2_entry, virt_addr); diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index 874d234f7..28eb097d3 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -257,7 +257,7 @@ namespace ams::kern::board::nintendo::nx { const WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]); this->state[i] |= clear_bit; out[num_reserved++] = static_cast(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit)); - R_UNLESS(num_reserved != num_desired, ResultSuccess()); + R_SUCCEED_IF(num_reserved == num_desired); } } diff --git a/libraries/libmesosphere/source/kern_k_capabilities.cpp b/libraries/libmesosphere/source/kern_k_capabilities.cpp index 5e9b36356..871721108 100644 --- a/libraries/libmesosphere/source/kern_k_capabilities.cpp +++ b/libraries/libmesosphere/source/kern_k_capabilities.cpp @@ -211,7 +211,9 @@ namespace ams::kern { /* Validate this is a capability we can act on. */ const auto type = GetCapabilityType(cap); R_UNLESS(type != CapabilityType::Invalid, svc::ResultInvalidArgument()); - R_UNLESS(type != CapabilityType::Padding, ResultSuccess()); + + /* If the type is padding, we have no work to do. */ + R_SUCCEED_IF(type == CapabilityType::Padding); /* Check that we haven't already processed this capability. */ const auto flag = GetCapabilityFlag(type); diff --git a/libraries/libmesosphere/source/kern_k_page_group.cpp b/libraries/libmesosphere/source/kern_k_page_group.cpp index 3e81eb593..1340064c9 100644 --- a/libraries/libmesosphere/source/kern_k_page_group.cpp +++ b/libraries/libmesosphere/source/kern_k_page_group.cpp @@ -38,7 +38,7 @@ namespace ams::kern { Result KPageGroup::AddBlock(KVirtualAddress addr, size_t num_pages) { /* Succeed immediately if we're adding no pages. */ - R_UNLESS(num_pages != 0, ResultSuccess()); + R_SUCCEED_IF(num_pages == 0); /* Check for overflow. */ MESOSPHERE_ASSERT(addr < addr + num_pages * PageSize); @@ -46,7 +46,7 @@ namespace ams::kern { /* Try to just append to the last block. */ if (!this->block_list.empty()) { auto it = --(this->block_list.end()); - R_UNLESS(!it->TryConcatenate(addr, num_pages), ResultSuccess()); + R_SUCCEED_IF(it->TryConcatenate(addr, num_pages)); } /* Allocate a new block. */ diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 0129f75a6..bc308430d 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -28,8 +28,8 @@ #include "stratosphere/dd.hpp" #include "stratosphere/lmem.hpp" -/* Lots of things depend on NCM, for Program IDs. */ -#include "stratosphere/ncm.hpp" +/* Pull in all ID definitions from NCM. */ +#include "stratosphere/ncm/ncm_ids.hpp" /* At this point, just include the rest alphabetically. */ /* TODO: Figure out optimal order. */ @@ -41,7 +41,9 @@ #include "stratosphere/hos.hpp" #include "stratosphere/kvdb.hpp" #include "stratosphere/ldr.hpp" +#include "stratosphere/lr.hpp" #include "stratosphere/map.hpp" +#include "stratosphere/ncm.hpp" #include "stratosphere/patcher.hpp" #include "stratosphere/pm.hpp" #include "stratosphere/reg.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp b/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp index e4e2292f6..6f2bffc40 100644 --- a/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp @@ -15,8 +15,8 @@ */ #pragma once -#include "../os/os_common_types.hpp" -#include "../ncm/ncm_types.hpp" +#include +#include namespace ams::cfg { diff --git a/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp b/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp index 576fc411d..017e73407 100644 --- a/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp @@ -15,9 +15,9 @@ */ #pragma once -#include "../os/os_common_types.hpp" -#include "../ncm/ncm_types.hpp" -#include "../sf/sf_buffer_tags.hpp" +#include +#include +#include namespace ams::dmnt::cheat { diff --git a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp index ac429fc4a..592834889 100644 --- a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp @@ -16,8 +16,8 @@ #pragma once #include -#include "../ncm/ncm_types.hpp" -#include "../sf/sf_buffer_tags.hpp" +#include +#include namespace ams::fatal { diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index 76c844a92..1ed062124 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -19,11 +19,31 @@ #include #include #include +#include +#include #include #include #include +#include +#include #include #include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp new file mode 100644 index 000000000..a6831973e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::fs { + + enum class BisPartitionId { + /* Boot0 */ + BootPartition1Root = 0, + + /* Boot1 */ + BootPartition2Root = 10, + + /* Non-Boot */ + UserDataRoot = 20, + BootConfigAndPackage2Part1 = 21, + BootConfigAndPackage2Part2 = 22, + BootConfigAndPackage2Part3 = 23, + BootConfigAndPackage2Part4 = 24, + BootConfigAndPackage2Part5 = 25, + BootConfigAndPackage2Part6 = 26, + CalibrationBinary = 27, + CalibrationFile = 28, + SafeMode = 29, + User = 30, + System = 31, + SystemProperEncryption = 32, + SystemProperPartition = 33, + SignedSystemPartitionOnSafeMode = 34, + }; + + const char *GetBisMountName(BisPartitionId id); + + Result MountBis(BisPartitionId id, const char *root_path); + Result MountBis(const char *name, BisPartitionId id); + + void SetBisRootForHost(BisPartitionId id, const char *root_path); + + Result OpenBisPartition(std::unique_ptr *out, BisPartitionId id); + + Result InvalidateBisCache(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp new file mode 100644 index 000000000..e151f94ba --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include + +namespace ams::fs { + + Result MountCode(const char *name, const char *path, ncm::ProgramId program_id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp index 13b0d5a14..4f6a6436d 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp @@ -15,13 +15,34 @@ */ #pragma once #include -#include "../os.hpp" -#include "../ncm.hpp" -#include "../sf.hpp" +#include +#include +#include namespace ams::fs { - /* TODO: Better place for this? */ - constexpr inline size_t MountNameLengthMax = 15; + struct Int64 { + u32 low; + u32 high; -} + constexpr ALWAYS_INLINE void Set(s64 v) { + this->low = static_cast((v & static_cast(0x00000000FFFFFFFFul)) >> 0); + this->high = static_cast((v & static_cast(0xFFFFFFFF00000000ul)) >> 32); + } + + constexpr ALWAYS_INLINE s64 Get() const { + return (static_cast(this->high) << 32) | (static_cast(this->low)); + } + + constexpr ALWAYS_INLINE Int64 &operator=(s64 v) { + this->Set(v); + return *this; + } + + constexpr ALWAYS_INLINE operator s64() const { + return this->Get(); + } + }; + static_assert(std::is_pod::value); + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_content.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_content.hpp new file mode 100644 index 000000000..32a2eded9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_content.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include + +namespace ams::fs { + + enum ContentType { + ContentType_Meta = 0, + ContentType_Control = 1, + ContentType_Manual = 2, + ContentType_Logo = 3, + ContentType_Data = 4, + }; + + Result MountContent(const char *name, const char *path, ContentType content_type); + Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type); + Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp new file mode 100644 index 000000000..4a52391e2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + enum class ContentStorageId : u32 { + System = 0, + User = 1, + SdCard = 2, + }; + + constexpr inline const char * const ContentStorageDirectoryName = "Contents"; + + const char *GetContentStorageMountName(ContentStorageId id); + + Result MountContentStorage(ContentStorageId id); + Result MountContentStorage(const char *name, ContentStorageId id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp new file mode 100644 index 000000000..d2306154b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_dbm_rom_types.hpp" +#include "fs_dbm_rom_path_tool.hpp" +#include "fs_dbm_rom_key_value_storage.hpp" + +namespace ams::fs { + + class HierarchicalRomFileTable { + public: + using Position = u32; + + struct FindPosition { + Position next_dir; + Position next_file; + }; + static_assert(std::is_pod::value); + + using DirectoryInfo = RomDirectoryInfo; + using FileInfo = RomFileInfo; + + static constexpr RomFileId ConvertToFileId(Position pos) { + return static_cast(pos); + } + private: + static constexpr inline Position InvalidPosition = ~Position(); + static constexpr inline Position RootPosition = 0; + static constexpr inline size_t ReservedDirectoryCount = 1; + + static constexpr RomDirectoryId ConvertToDirectoryId(Position pos) { + return static_cast(pos); + } + + static constexpr Position ConvertToPosition(RomDirectoryId id) { + return static_cast(id); + } + + static_assert(std::is_same::value); + + struct RomDirectoryEntry { + Position next; + Position dir; + Position file; + }; + static_assert(std::is_pod::value); + + struct RomFileEntry { + Position next; + FileInfo info; + }; + static_assert(std::is_pod::value); + + static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength; + + template + class EntryMapTable : public RomKeyValueStorage { + public: + using ImplKey = ImplKeyType; + using ClientKey = ClientKeyType; + using Value = ValueType; + using Position = HierarchicalRomFileTable::Position; + using Base = RomKeyValueStorage; + public: + Result Add(Position *out, const ClientKeyType &key, const Value &value) { + return Base::AddImpl(out, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar), value); + } + + Result Get(Position *out_pos, Value *out_val, const ClientKeyType &key) { + return Base::GetImpl(out_pos, out_val, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar)); + } + + Result GetByPosition(ImplKey *out_key, Value *out_val, Position pos) { + return Base::GetByPosition(out_key, out_val, pos); + } + + Result GetByPosition(ImplKey *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) { + return Base::GetByPosition(out_key, out_val, out_aux, out_aux_size, pos); + } + + Result SetByPosition(Position pos, const Value &value) { + return Base::SetByPosition(pos, value); + } + }; + + struct RomEntryKey { + Position parent; + + bool IsEqual(const RomEntryKey &rhs, const void *aux_lhs, size_t aux_lhs_size, const void *aux_rhs, size_t aux_rhs_size) const { + if (this->parent != rhs.parent) { + return false; + } + if (aux_lhs_size != aux_rhs_size) { + return false; + } + return RomPathTool::IsEqualPath(reinterpret_cast(aux_lhs), reinterpret_cast(aux_rhs), aux_lhs_size / sizeof(RomPathChar)); + } + }; + static_assert(std::is_pod::value); + + struct EntryKey { + RomEntryKey key; + RomPathTool::RomEntryName name; + + constexpr u32 Hash() const { + u32 hash = this->key.parent ^ 123456789; + const RomPathChar *name = this->name.path; + const RomPathChar *end = name + this->name.length; + while (name < end) { + const u32 cur = static_cast(static_cast::type>(*(name++))); + hash = ((hash >> 5) | (hash << 27)) ^ cur; + } + return hash; + } + }; + static_assert(std::is_pod::value); + + using DirectoryEntryMapTable = EntryMapTable; + using FileEntryMapTable = EntryMapTable; + private: + DirectoryEntryMapTable dir_table; + FileEntryMapTable file_table; + public: + static s64 QueryDirectoryEntryStorageSize(u32 count); + static s64 QueryDirectoryEntryBucketStorageSize(s64 count); + static s64 QueryFileEntryStorageSize(u32 count); + static s64 QueryFileEntryBucketStorageSize(s64 count); + + static Result Format(SubStorage dir_bucket, SubStorage file_bucket); + public: + HierarchicalRomFileTable(); + + constexpr u32 GetDirectoryEntryCount() const { + return this->dir_table.GetEntryCount(); + } + + constexpr u32 GetFileEntryCount() const { + return this->file_table.GetEntryCount(); + } + + Result Initialize(SubStorage dir_bucket, SubStorage dir_entry, SubStorage file_bucket, SubStorage file_entry); + void Finalize(); + + Result CreateRootDirectory(); + Result CreateDirectory(RomDirectoryId *out, const RomPathChar *path, const DirectoryInfo &info); + Result CreateFile(RomFileId *out, const RomPathChar *path, const FileInfo &info); + Result ConvertPathToDirectoryId(RomDirectoryId *out, const RomPathChar *path); + Result ConvertPathToFileId(RomFileId *out, const RomPathChar *path); + + Result GetDirectoryInformation(DirectoryInfo *out, const RomPathChar *path); + Result GetDirectoryInformation(DirectoryInfo *out, RomDirectoryId id); + + Result OpenFile(FileInfo *out, const RomPathChar *path); + Result OpenFile(FileInfo *out, RomFileId id); + + Result FindOpen(FindPosition *out, const RomPathChar *path); + Result FindOpen(FindPosition *out, RomDirectoryId id); + + Result FindNextDirectory(RomPathChar *out, FindPosition *find, size_t length); + Result FindNextFile(RomPathChar *out, FindPosition *find, size_t length); + + Result QueryRomFileSystemSize(s64 *out_dir_entry_size, s64 *out_file_entry_size); + private: + Result GetGrandParent(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, Position pos, RomPathTool::RomEntryName name, const RomPathChar *path); + + Result FindParentDirectoryRecursive(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, RomPathTool::PathParser *parser, const RomPathChar *path); + + Result FindPathRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, bool is_dir, const RomPathChar *path); + Result FindDirectoryRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path); + Result FindFileRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path); + + Result CheckSameEntryExists(const EntryKey &key, Result if_exists); + + Result GetDirectoryEntry(Position *out_pos, RomDirectoryEntry *out_entry, const EntryKey &key); + Result GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id); + + Result GetFileEntry(Position *out_pos, RomFileEntry *out_entry, const EntryKey &key); + Result GetFileEntry(RomFileEntry *out_entry, RomFileId id); + + Result GetDirectoryInformation(DirectoryInfo *out, const EntryKey &key); + + Result OpenFile(FileInfo *out, const EntryKey &key); + + Result FindOpen(FindPosition *out, const EntryKey &key); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp new file mode 100644 index 000000000..e52b260e5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_dbm_rom_types.hpp" +#include "fs_substorage.hpp" + +namespace ams::fs { + + template + class RomKeyValueStorage { + public: + using Key = KeyType; + using Value = ValueType; + using Position = u32; + using BucketIndex = s64; + + struct FindIndex { + BucketIndex ind; + Position pos; + }; + static_assert(std::is_pod::value); + private: + static constexpr inline Position InvalidPosition = ~Position(); + + struct Element { + Key key; + Value value; + Position next; + u32 size; + }; + static_assert(std::is_pod::value); + private: + s64 bucket_count; + SubStorage bucket_storage; + SubStorage kv_storage; + s64 total_entry_size; + u32 entry_count; + public: + static constexpr s64 QueryBucketStorageSize(s64 num) { + return num * sizeof(Position); + } + + static constexpr s64 QueryBucketCount(s64 size) { + return size / sizeof(Position); + } + + static constexpr s64 QueryKeyValueStorageSize(u32 num) { + return num * sizeof(Element); + } + + static Result Format(SubStorage bucket, s64 count) { + const Position pos = InvalidPosition; + for (s64 i = 0; i < count; i++) { + R_TRY(bucket.Write(i * sizeof(pos), std::addressof(pos), sizeof(pos))); + } + return ResultSuccess(); + } + public: + RomKeyValueStorage() : bucket_count(), bucket_storage(), kv_storage(), total_entry_size(), entry_count() { /* ... */ } + + Result Initialize(const SubStorage &bucket, s64 count, const SubStorage &kv) { + AMS_ASSERT(count > 0); + this->bucket_storage = bucket; + this->kv_storage = kv; + this->bucket_count = count; + return ResultSuccess(); + } + + void Finalize() { + this->bucket_storage = SubStorage(); + this->kv_storage = SubStorage(); + this->bucket_count = 0; + } + + s64 GetTotalEntrySize() const { + return this->total_entry_size; + } + + Result GetFreeSize(s64 *out) { + AMS_ASSERT(out != nullptr); + s64 kv_size = 0; + R_TRY(this->kv_storage.GetSize(std::addressof(kv_size))); + *out = kv_size - this->total_entry_size; + return ResultSuccess(); + } + + constexpr u32 GetEntryCount() const { + return this->entry_count; + } + + Result Add(const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) { + AMS_ASSERT(aux != nullptr); + AMS_ASSERT(aux_size <= MaxAuxiliarySize); + Position pos; + return this->AddImpl(std::addressof(pos), key, hash_key, aux, aux_size, value); + } + + Result Get(Value *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size) { + AMS_ASSERT(aux != nullptr); + AMS_ASSERT(aux_size <= MaxAuxiliarySize); + Position pos; + return this->GetImpl(std::addressof(pos), out, key, hash_key, aux, aux_size); + } + + Result FindOpen(FindIndex *out) const { + AMS_ASSERT(out != nullptr); + + out->ind = static_cast(-1); + out->pos = InvalidPosition; + return ResultSuccess(); + } + + Result FindNext(Key *out_key, Value *out_val, FindIndex *find) { + AMS_ASSERT(out_key != nullptr); + AMS_ASSERT(out_val != nullptr); + AMS_ASSERT(find != nullptr); + + BucketIndex ind = find->ind; + R_UNLESS((ind < this->bucket_count) || ind == static_cast(-1), fs::ResultDbmFindKeyFinished()); + + s64 kv_size; + R_TRY(this->kv_storage.GetSize(std::addressof(kv_size))); + + while (true) { + if (find->pos != InvalidPosition) { + Element elem; + R_TRY(this->ReadKeyValue(std::addressof(elem), find->pos)); + + AMS_ASSERT(elem.next == InvalidPosition || elem.next < kv_size); + find->pos = elem.next; + *out_key = elem.key; + *out_val = elem.val; + return ResultSuccess(); + } + + while (true) { + ind++; + if (ind == this->bucket_count) { + find->ind = ind; + find->pos = InvalidPosition; + return fs::ResultDbmFindKeyFinished(); + } + + Position pos; + R_TRY(this->ReadBucket(std::addressof(pos), ind)); + AMS_ASSERT(pos == InvalidPosition || pos < kv_size); + + if (pos != InvalidPosition) { + find->ind = ind; + find->pos = pos; + break; + } + } + } + } + protected: + Result AddImpl(Position *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(aux != nullptr); + AMS_ASSERT(this->bucket_count > 0); + + { + Position pos, prev_pos; + Element elem; + + const Result find_res = this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size); + R_UNLESS(R_FAILED(find_res), fs::ResultDbmAlreadyExists()); + R_UNLESS(fs::ResultDbmKeyNotFound::Includes(find_res), find_res); + } + + Position pos; + R_TRY(this->AllocateEntry(std::addressof(pos), aux_size)); + + Position next_pos; + R_TRY(this->LinkEntry(std::addressof(next_pos), pos, hash_key)); + + const Element elem = { key, value, next_pos, static_cast(aux_size) }; + R_TRY(this->WriteKeyValue(std::addressof(elem), pos, aux, aux_size)); + + *out = pos; + this->entry_count++; + + return ResultSuccess(); + } + + Result GetImpl(Position *out_pos, Value *out_val, const Key &key, u32 hash_key, const void *aux, size_t aux_size) { + AMS_ASSERT(out_pos != nullptr); + AMS_ASSERT(out_val != nullptr); + AMS_ASSERT(aux != nullptr); + + Position pos, prev_pos; + Element elem; + R_TRY(this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size)); + + *out_pos = pos; + *out_val = elem.value; + return ResultSuccess(); + } + + Result GetByPosition(Key *out_key, Value *out_val, Position pos) { + AMS_ASSERT(out_key != nullptr); + AMS_ASSERT(out_val != nullptr); + + Element elem; + R_TRY(this->ReadKeyValue(std::addressof(elem), pos)); + + *out_key = elem.key; + *out_val = elem.value; + return ResultSuccess(); + } + + Result GetByPosition(Key *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) { + AMS_ASSERT(out_key != nullptr); + AMS_ASSERT(out_val != nullptr); + AMS_ASSERT(out_aux != nullptr); + AMS_ASSERT(out_aux_size != nullptr); + + Element elem; + R_TRY(this->ReadKeyValue(std::addressof(elem), out_aux, out_aux_size, pos)); + + *out_key = elem.key; + *out_val = elem.value; + return ResultSuccess(); + } + + Result SetByPosition(Position pos, const Value &value) { + Element elem; + R_TRY(this->ReadKeyValue(std::addressof(elem), pos)); + elem.value = value; + return this->WriteKeyValue(std::addressof(elem), pos, nullptr, 0); + } + private: + BucketIndex HashToBucket(u32 hash_key) const { + return hash_key % this->bucket_count; + } + + Result FindImpl(Position *out_pos, Position *out_prev, Element *out_elem, const Key &key, u32 hash_key, const void *aux, size_t aux_size) { + AMS_ASSERT(out_pos != nullptr); + AMS_ASSERT(out_prev != nullptr); + AMS_ASSERT(out_elem != nullptr); + AMS_ASSERT(aux != nullptr); + AMS_ASSERT(this->bucket_count > 0); + + *out_pos = 0; + *out_prev = 0; + + const BucketIndex ind = HashToBucket(hash_key); + + Position cur; + R_TRY(this->ReadBucket(std::addressof(cur), ind)); + + s64 kv_size; + R_TRY(this->kv_storage.GetSize(std::addressof(kv_size))); + AMS_ASSERT(cur == InvalidPosition || cur < kv_size); + + R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound()); + + u8 *buf = static_cast(::ams::fs::impl::Allocate(MaxAuxiliarySize)); + R_UNLESS(buf != nullptr, fs::ResultAllocationFailureInDbmRomKeyValueStorage()); + ON_SCOPE_EXIT { ::ams::fs::impl::Deallocate(buf, MaxAuxiliarySize); }; + + while (true) { + size_t cur_aux_size; + R_TRY(this->ReadKeyValue(out_elem, buf, std::addressof(cur_aux_size), cur)); + + if (key.IsEqual(out_elem->key, aux, aux_size, buf, cur_aux_size)) { + *out_pos = cur; + return ResultSuccess(); + } + + *out_prev = cur; + cur = out_elem->next; + R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound()); + } + } + + Result AllocateEntry(Position *out, size_t aux_size) { + AMS_ASSERT(out != nullptr); + + s64 kv_size; + R_TRY(this->kv_storage.GetSize(std::addressof(kv_size))); + const size_t end_pos = this->total_entry_size + sizeof(Element) + aux_size; + R_UNLESS(end_pos <= static_cast(kv_size), fs::ResultDbmKeyFull()); + + *out = static_cast(this->total_entry_size); + + this->total_entry_size = util::AlignUp(static_cast(end_pos), s64(4)); + return ResultSuccess(); + } + + Result LinkEntry(Position *out, Position pos, u32 hash_key) { + AMS_ASSERT(out != nullptr); + + const BucketIndex ind = HashToBucket(hash_key); + + Position next; + R_TRY(this->ReadBucket(std::addressof(next), ind)); + + s64 kv_size; + R_TRY(this->kv_storage.GetSize(std::addressof(kv_size))); + AMS_ASSERT(next == InvalidPosition || next < kv_size); + + R_TRY(this->WriteBucket(pos, ind)); + + *out = next; + return ResultSuccess(); + } + + Result ReadBucket(Position *out, BucketIndex ind) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(ind < this->bucket_count); + + const s64 offset = ind * sizeof(Position); + return this->bucket_storage.Read(offset, out, sizeof(*out)); + } + + Result WriteBucket(Position pos, BucketIndex ind) { + AMS_ASSERT(ind < this->bucket_count); + + const s64 offset = ind * sizeof(Position); + return this->bucket_storage.Write(offset, std::addressof(pos), sizeof(pos)); + } + + Result ReadKeyValue(Element *out, Position pos) { + AMS_ASSERT(out != nullptr); + + s64 kv_size; + R_TRY(this->kv_storage.GetSize(std::addressof(kv_size))); + AMS_ASSERT(pos < kv_size); + + return this->kv_storage.Read(pos, out, sizeof(*out)); + } + + Result ReadKeyValue(Element *out, void *out_aux, size_t *out_aux_size, Position pos) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(out_aux != nullptr); + AMS_ASSERT(out_aux_size != nullptr); + + R_TRY(this->ReadKeyValue(out, pos)); + + *out_aux_size = out->size; + if (out->size > 0) { + R_TRY(this->kv_storage.Read(pos + sizeof(*out), out_aux, out->size)); + } + + return ResultSuccess(); + } + + Result WriteKeyValue(const Element *elem, Position pos, const void *aux, size_t aux_size) { + AMS_ASSERT(elem != nullptr); + AMS_ASSERT(aux != nullptr); + + s64 kv_size; + R_TRY(this->kv_storage.GetSize(std::addressof(kv_size))); + AMS_ASSERT(pos < kv_size); + + R_TRY(this->kv_storage.Write(pos, elem, sizeof(*elem))); + + if (aux != nullptr && aux_size > 0) { + R_TRY(this->kv_storage.Write(pos + sizeof(*elem), aux, aux_size)); + } + + return ResultSuccess(); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp new file mode 100644 index 000000000..c57b1fe9d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_dbm_rom_types.hpp" + +namespace ams::fs { + + namespace RomPathTool { + + constexpr inline u32 MaxPathLength = 0x300; + + struct RomEntryName { + size_t length; + const RomPathChar *path; + }; + static_assert(std::is_pod::value); + + constexpr void InitializeRomEntryName(RomEntryName *entry) { + AMS_ABORT_UNLESS(entry != nullptr); + entry->length = 0; + } + + constexpr inline bool IsSeparator(RomPathChar c) { + return c == RomStringTraits::DirectorySeparator; + } + + constexpr inline bool IsNullTerminator(RomPathChar c) { + return c == RomStringTraits::NullTerminator; + } + + constexpr inline bool IsDot(RomPathChar c) { + return c == RomStringTraits::Dot; + } + + constexpr inline bool IsCurrentDirectory(const RomEntryName &name) { + return name.length == 1 && IsDot(name.path[0]); + } + + constexpr inline bool IsCurrentDirectory(const RomPathChar *p, size_t length) { + AMS_ABORT_UNLESS(p != nullptr); + return length == 1 && IsDot(p[0]); + } + + constexpr inline bool IsCurrentDirectory(const RomPathChar *p) { + AMS_ABORT_UNLESS(p != nullptr); + return IsDot(p[0]) && IsNullTerminator(p[1]); + } + + constexpr inline bool IsParentDirectory(const RomEntryName &name) { + return name.length == 2 && IsDot(name.path[0]) && IsDot(name.path[1]); + } + + constexpr inline bool IsParentDirectory(const RomPathChar *p) { + AMS_ABORT_UNLESS(p != nullptr); + return IsDot(p[0]) && IsDot(p[1]) && IsNullTerminator(p[2]); + } + + constexpr inline bool IsParentDirectory(const RomPathChar *p, size_t length) { + AMS_ABORT_UNLESS(p != nullptr); + return length == 2 && IsDot(p[0]) && IsDot(p[1]); + } + + constexpr inline bool IsEqualPath(const RomPathChar *lhs, const RomPathChar *rhs, size_t length) { + AMS_ABORT_UNLESS(lhs != nullptr); + AMS_ABORT_UNLESS(rhs != nullptr); + return std::strncmp(lhs, rhs, length) == 0; + } + + constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomPathChar *rhs) { + AMS_ABORT_UNLESS(rhs != nullptr); + if (strnlen(rhs, MaxPathLength) != lhs.length) { + return false; + } + return IsEqualPath(lhs.path, rhs, lhs.length); + } + + constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomEntryName &rhs) { + if (lhs.length != rhs.length) { + return false; + } + return IsEqualPath(lhs.path, rhs.path, lhs.length); + } + + Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p); + + class PathParser { + private: + const RomPathChar *prev_path_start; + const RomPathChar *prev_path_end; + const RomPathChar *next_path; + bool finished; + public: + constexpr PathParser() : prev_path_start(), prev_path_end(), next_path(), finished() { /* ... */ } + + Result Initialize(const RomPathChar *path); + void Finalize(); + + bool IsFinished() const; + bool IsDirectoryPath() const; + + Result GetAsDirectoryName(RomEntryName *out) const; + Result GetAsFileName(RomEntryName *out) const; + + Result GetNextDirectoryName(RomEntryName *out); + }; + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp new file mode 100644 index 000000000..86a646e45 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + using RomPathChar = char; + using RomFileId = s32; + using RomDirectoryId = s32; + + struct RomFileSystemInformation { + s64 size; + s64 directory_bucket_offset; + s64 directory_bucket_size; + s64 directory_entry_offset; + s64 directory_entry_size; + s64 file_bucket_offset; + s64 file_bucket_size; + s64 file_entry_offset; + s64 file_entry_size; + s64 body_offset; + }; + static_assert(std::is_pod::value); + static_assert(sizeof(RomFileSystemInformation) == 0x50); + + struct RomDirectoryInfo { + /* ... */ + }; + static_assert(std::is_pod::value); + + struct RomFileInfo { + Int64 offset; + Int64 size; + }; + static_assert(std::is_pod::value); + + namespace RomStringTraits { + + constexpr inline char DirectorySeparator = '/'; + constexpr inline char NullTerminator = '\x00'; + constexpr inline char Dot = '.'; + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_directory.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_directory.hpp index 65e84ff01..c2e3a76b1 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_directory.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_directory.hpp @@ -22,4 +22,12 @@ namespace ams::fs { using DirectoryEntry = ::FsDirectoryEntry; + struct DirectoryHandle { + void *handle; + }; + + Result ReadDirectory(s64 *out_count, DirectoryEntry *out_entries, DirectoryHandle handle, s64 max_entries); + Result GetDirectoryEntryCount(s64 *out, DirectoryHandle handle); + void CloseDirectory(DirectoryHandle handle); + } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp index f8515f1f7..51fab7070 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp @@ -60,4 +60,19 @@ namespace ams::fs { static_assert(std::is_pod::value && sizeof(WriteOption) == sizeof(u32)); + struct FileHandle { + void *handle; + }; + + Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option); + Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size); + Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option); + Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size); + Result GetFileSize(s64 *out, FileHandle handle); + Result FlushFile(FileHandle handle); + Result WriteFile(FileHandle handle, s64 offset, const void *buffer, size_t size, const fs::WriteOption &option); + Result SetFileSize(FileHandle handle, s64 size); + int GetFileOpenMode(FileHandle handle); + void CloseFile(FileHandle handle); + } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_filesystem.hpp index 2c8767f0f..79a77c505 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_filesystem.hpp @@ -18,6 +18,12 @@ namespace ams::fs { + namespace fsa { + + class IFile; + + } + enum OpenMode { OpenMode_Read = ::FsOpenMode_Read, OpenMode_Write = ::FsOpenMode_Write, @@ -49,4 +55,27 @@ namespace ams::fs { using FileTimeStampRaw = ::FsTimeStampRaw; + struct FileHandle; + struct DirectoryHandle; + + Result CreateFile(const char *path, s64 size); + Result CreateFile(const char* path, s64 size, int option); + Result DeleteFile(const char *path); + Result CreateDirectory(const char *path); + Result DeleteDirectory(const char *path); + Result DeleteDirectoryRecursively(const char *path); + Result RenameFile(const char *old_path, const char *new_path); + Result RenameDirectory(const char *old_path, const char *new_path); + Result GetEntryType(DirectoryEntryType *out, const char *path); + Result OpenFile(FileHandle *out_file, const char *path, int mode); + Result OpenDirectory(DirectoryHandle *out_dir, const char *path, int mode); + Result CleanDirectoryRecursively(const char *path); + Result GetFreeSpaceSize(s64 *out, const char *path); + Result GetTotalSpaceSize(s64 *out, const char *path); + + Result SetConcatenationFileAttribute(const char *path); + Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path); + + Result OpenFile(FileHandle *out, std::unique_ptr &&file, int mode); + } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp new file mode 100644 index 000000000..612b7844a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + enum class GameCardPartition { + Update = 0, + Normal = 1, + Secure = 2, + Logo = 3, + }; + + enum class GameCardPartitionRaw { + NormalReadable, + SecureReadable, + RootWriteable, + }; + + enum class GameCardAttribute : u8 { + AutoBootFlag = (1 << 0), + HistoryEraseFlag = (1 << 1), + RepairToolFlag = (1 << 2), + DifferentRegionCupToTerraDeviceFlag = (1 << 3), + DifferentRegionCupToGlobalDeviceFlag = (1 << 4), + }; + + using GameCardHandle = u32; + + Result GetGameCardHandle(GameCardHandle *out); + Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_istorage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_istorage.hpp index b3d06e8b8..d6749c5c3 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_istorage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_istorage.hpp @@ -26,21 +26,15 @@ namespace ams::fs { virtual Result Read(s64 offset, void *buffer, size_t size) = 0; - virtual Result Write(s64 offset, const void *buffer, size_t size) { - return fs::ResultUnsupportedOperation(); - } + virtual Result Write(s64 offset, const void *buffer, size_t size) = 0; virtual Result Flush() = 0; - virtual Result SetSize(s64 size) { - return fs::ResultUnsupportedOperation(); - } + virtual Result SetSize(s64 size) = 0; virtual Result GetSize(s64 *out) = 0; - virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { - return fs::ResultUnsupportedOperation(); - } + virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) = 0; virtual Result OperateRange(OperationId op_id, s64 offset, s64 size) { return this->OperateRange(nullptr, 0, op_id, offset, size, nullptr, 0); @@ -86,21 +80,31 @@ namespace ams::fs { virtual ~ReadOnlyStorageAdapter() { /* ... */ } public: - virtual Result Read(s64 offset, void *buffer, size_t size) { + virtual Result Read(s64 offset, void *buffer, size_t size) override { return this->storage->Read(offset, buffer, size); } - virtual Result Flush() { + virtual Result Flush() override { return this->storage->Flush(); } - virtual Result GetSize(s64 *out) { + virtual Result GetSize(s64 *out) override { return this->storage->GetSize(out); } virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { return this->storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* TODO: Better result? Is it possible to get a more specific one? */ + return fs::ResultUnsupportedOperation(); + } + + virtual Result SetSize(s64 size) override { + /* TODO: Better result? Is it possible to get a more specific one? */ + return fs::ResultUnsupportedOperation(); + } }; } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp new file mode 100644 index 000000000..cc9a31a2f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + using AllocateFunction = void *(*)(size_t); + using DeallocateFunction = void (*)(void *, size_t); + + void SetAllocator(AllocateFunction allocator, DeallocateFunction deallocator); + + namespace impl { + + void *Allocate(size_t size); + void Deallocate(void *ptr, size_t size); + + class Deleter { + private: + size_t size; + public: + Deleter() : size() { /* ... */ } + explicit Deleter(size_t sz) : size(sz) { /* ... */ } + + void operator()(void *ptr) const { + ::ams::fs::impl::Deallocate(ptr, this->size); + } + }; + + template + std::unique_ptr MakeUnique() { + static_assert(std::is_pod::value); + return std::unique_ptr(static_cast(::ams::fs::impl::Allocate(sizeof(T))), Deleter(sizeof(T))); + } + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp new file mode 100644 index 000000000..b897c535e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_storage.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "impl/fs_newable.hpp" +#include "fs_istorage.hpp" +#include "fs_query_range.hpp" + +namespace ams::fs { + + class MemoryStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + private: + u8 * const buf; + const s64 size; + public: + MemoryStorage(void *b, s64 sz) : buf(static_cast(b)), size(sz) { /* .. */ } + public: + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Succeed immediately on zero-sized read. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); + + /* Copy from memory. */ + std::memcpy(buffer, this->buf + offset, size); + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* Succeed immediately on zero-sized write. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); + + /* Copy to memory. */ + std::memcpy(this->buf + offset, buffer, size); + return ResultSuccess(); + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result GetSize(s64 *out) override { + *out = this->size; + return ResultSuccess(); + } + + virtual Result SetSize(s64 size) override { + return fs::ResultUnsupportedOperationInMemoryStorageA(); + } + + virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + switch (op_id) { + case OperationId::InvalidateCache: + return ResultSuccess(); + case OperationId::QueryRange: + R_UNLESS(dst != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize()); + reinterpret_cast(dst)->Clear(); + return ResultSuccess(); + default: + return fs::ResultUnsupportedOperationInMemoryStorageB(); + } + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_mount.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_mount.hpp new file mode 100644 index 000000000..1344b2a4d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_mount.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + constexpr inline size_t MountNameLengthMax = 15; + + Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *src); + + void Unmount(const char *mount_name); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp index 2344dac91..37f2c5b5d 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp @@ -26,16 +26,25 @@ namespace ams::fs { constexpr inline char Dot = '.'; constexpr inline char NullTerminator = '\x00'; + constexpr inline char AlternateDirectorySeparator = '\\'; } class PathTool { public: static constexpr const char RootPath[] = "/"; public: + static constexpr inline bool IsAlternateSeparator(char c) { + return c == StringTraits::AlternateDirectorySeparator; + } + static constexpr inline bool IsSeparator(char c) { return c == StringTraits::DirectorySeparator; } + static constexpr inline bool IsAnySeparator(char c) { + return IsSeparator(c) || IsAlternateSeparator(c); + } + static constexpr inline bool IsNullTerminator(char c) { return c == StringTraits::NullTerminator; } @@ -56,6 +65,10 @@ namespace ams::fs { return IsWindowsDriveCharacter(p[0]) && IsDriveSeparator(p[1]); } + static constexpr inline bool IsUnc(const char *p) { + return (IsSeparator(p[0]) && IsSeparator(p[1])) || (IsAlternateSeparator(p[0]) && IsAlternateSeparator(p[1])); + } + static constexpr inline bool IsCurrentDirectory(const char *p) { return IsDot(p[0]) && (IsSeparator(p[1]) || IsNullTerminator(p[1])); } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp index f6255edde..6777f03cf 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp @@ -15,6 +15,7 @@ */ #pragma once #include "fs_common.hpp" +#include "fs_file.hpp" namespace ams::fs { @@ -42,4 +43,6 @@ namespace ams::fs { using FileQueryRangeInfo = QueryRangeInfo; using StorageQueryRangeInfo = QueryRangeInfo; + Result QueryRange(QueryRangeInfo *out, FileHandle handle, s64 offset, s64 size); + } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp index 08c38988b..824fc8432 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp @@ -14,165 +14,217 @@ * along with this program. If not, see . */ #pragma once -#include "fs_common.hpp" -#include "fsa/fs_ifile.hpp" -#include "fsa/fs_idirectory.hpp" -#include "fsa/fs_ifilesystem.hpp" +#include +#include +#include +#include +#include +#include +#include +#include namespace ams::fs { - class RemoteFile : public fsa::IFile { + class RemoteFile : public fsa::IFile, public impl::Newable { private: - std::unique_ptr<::FsFile> base_file; + ::FsFile base_file; public: - RemoteFile(::FsFile *f) : base_file(f) { /* ... */ } - RemoteFile(std::unique_ptr<::FsFile> f) : base_file(std::move(f)) { /* ... */ } - RemoteFile(::FsFile f) { - this->base_file = std::make_unique<::FsFile>(f); - } + RemoteFile(const ::FsFile &f) : base_file(f) { /* ... */ } - virtual ~RemoteFile() { fsFileClose(this->base_file.get()); } + virtual ~RemoteFile() { fsFileClose(std::addressof(this->base_file)); } public: virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final { - return fsFileRead(this->base_file.get(), offset, buffer, size, option.value, out); + return fsFileRead(std::addressof(this->base_file), offset, buffer, size, option.value, out); } virtual Result GetSizeImpl(s64 *out) override final { - return fsFileGetSize(this->base_file.get(), out); + return fsFileGetSize(std::addressof(this->base_file), out); } virtual Result FlushImpl() override final { - return fsFileFlush(this->base_file.get()); + return fsFileFlush(std::addressof(this->base_file)); } virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final { - return fsFileWrite(this->base_file.get(), offset, buffer, size, option.value); + return fsFileWrite(std::addressof(this->base_file), offset, buffer, size, option.value); } virtual Result SetSizeImpl(s64 size) override final { - return fsFileSetSize(this->base_file.get(), size); + return fsFileSetSize(std::addressof(this->base_file), size); } virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final { - /* TODO: How should this be handled? */ - return fs::ResultNotImplemented(); + R_UNLESS(op_id == OperationId::QueryRange, fs::ResultUnsupportedOperationInFileServiceObjectAdapterA()); + R_UNLESS(dst_size == sizeof(FileQueryRangeInfo), fs::ResultInvalidSize()); + + return fsFileOperateRange(std::addressof(this->base_file), static_cast<::FsOperationId>(op_id), offset, size, reinterpret_cast<::FsRangeInfo *>(dst)); } public: virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { - return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_file->s)}; + return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_file.s)))}; } }; - class RemoteDirectory : public fsa::IDirectory { + class RemoteDirectory : public fsa::IDirectory, public impl::Newable { private: - std::unique_ptr<::FsDir> base_dir; + ::FsDir base_dir; public: - RemoteDirectory(::FsDir *d) : base_dir(d) { /* ... */ } - RemoteDirectory(std::unique_ptr<::FsDir> d) : base_dir(std::move(d)) { /* ... */ } - RemoteDirectory(::FsDir d) { - this->base_dir = std::make_unique<::FsDir>(d); - } + RemoteDirectory(const ::FsDir &d) : base_dir(d) { /* ... */ } - virtual ~RemoteDirectory() { fsDirClose(this->base_dir.get()); } + virtual ~RemoteDirectory() { fsDirClose(std::addressof(this->base_dir)); } public: virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final { - return fsDirRead(this->base_dir.get(), out_count, max_entries, out_entries); + return fsDirRead(std::addressof(this->base_dir), out_count, max_entries, out_entries); } virtual Result GetEntryCountImpl(s64 *out) override final { - return fsDirGetEntryCount(this->base_dir.get(), out); + return fsDirGetEntryCount(std::addressof(this->base_dir), out); } public: virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { - return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_dir->s)}; + return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_dir.s)))}; } }; - class RemoteFileSystem : public fsa::IFileSystem { + class RemoteFileSystem : public fsa::IFileSystem, public impl::Newable { private: - std::unique_ptr<::FsFileSystem> base_fs; + ::FsFileSystem base_fs; public: - RemoteFileSystem(::FsFileSystem *fs) : base_fs(fs) { /* ... */ } - RemoteFileSystem(std::unique_ptr<::FsFileSystem> fs) : base_fs(std::move(fs)) { /* ... */ } - RemoteFileSystem(::FsFileSystem fs) { - this->base_fs = std::make_unique<::FsFileSystem>(fs); - } + RemoteFileSystem(const ::FsFileSystem &fs) : base_fs(fs) { /* ... */ } - virtual ~RemoteFileSystem() { fsFsClose(this->base_fs.get()); } + virtual ~RemoteFileSystem() { fsFsClose(std::addressof(this->base_fs)); } + private: + Result GetPathForServiceObject(fssrv::sf::Path *out_path, const char *path) { + /* Copy and null terminate. */ + std::strncpy(out_path->str, path, sizeof(out_path->str) - 1); + out_path->str[sizeof(out_path->str) - 1] = '\x00'; + + /* Replace directory separators. */ + Replace(out_path->str, sizeof(out_path->str) - 1, StringTraits::AlternateDirectorySeparator, StringTraits::DirectorySeparator); + + /* Get lengths. */ + const auto mount_name_len = PathTool::IsWindowsAbsolutePath(path) ? 2 : 0; + const auto rel_path = out_path->str + mount_name_len; + const auto max_len = fs::EntryNameLengthMax - mount_name_len; + return VerifyPath(rel_path, max_len, max_len); + } public: virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { - return fsFsCreateFile(this->base_fs.get(), path, size, flags); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsCreateFile(std::addressof(this->base_fs), sf_path.str, size, flags); } virtual Result DeleteFileImpl(const char *path) override final { - return fsFsDeleteFile(this->base_fs.get(), path); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsDeleteFile(std::addressof(this->base_fs), sf_path.str); } virtual Result CreateDirectoryImpl(const char *path) override final { - return fsFsCreateDirectory(this->base_fs.get(), path); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsCreateDirectory(std::addressof(this->base_fs), sf_path.str); } virtual Result DeleteDirectoryImpl(const char *path) override final { - return fsFsDeleteDirectory(this->base_fs.get(), path); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsDeleteDirectory(std::addressof(this->base_fs), sf_path.str); } virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { - return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsDeleteDirectoryRecursively(std::addressof(this->base_fs), sf_path.str); } virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { - return fsFsRenameFile(this->base_fs.get(), old_path, new_path); + fssrv::sf::Path old_sf_path; + fssrv::sf::Path new_sf_path; + R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path)); + R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path)); + return fsFsRenameFile(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str); } virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { - return fsFsRenameDirectory(this->base_fs.get(), old_path, new_path); + fssrv::sf::Path old_sf_path; + fssrv::sf::Path new_sf_path; + R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path)); + R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path)); + return fsFsRenameDirectory(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str); } virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + static_assert(sizeof(::FsDirEntryType) == sizeof(DirectoryEntryType)); - return fsFsGetEntryType(this->base_fs.get(), path, reinterpret_cast<::FsDirEntryType *>(out)); + return fsFsGetEntryType(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsDirEntryType *>(out)); } virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) override final { - FsFile f; - R_TRY(fsFsOpenFile(this->base_fs.get(), path, mode, &f)); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); - *out_file = std::make_unique(f); + FsFile f; + R_TRY(fsFsOpenFile(std::addressof(this->base_fs), sf_path.str, mode, &f)); + + auto file = std::make_unique(f); + R_UNLESS(file != nullptr, fs::ResultAllocationFailureInNew()); + + *out_file = std::move(file); return ResultSuccess(); } virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) override final { - FsDir d; - R_TRY(fsFsOpenDirectory(this->base_fs.get(), path, mode, &d)); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); - *out_dir = std::make_unique(d); + FsDir d; + R_TRY(fsFsOpenDirectory(std::addressof(this->base_fs), sf_path.str, mode, &d)); + + auto dir = std::make_unique(d); + R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInNew()); + + *out_dir = std::move(dir); return ResultSuccess(); } virtual Result CommitImpl() override final { - return fsFsCommit(this->base_fs.get()); + return fsFsCommit(std::addressof(this->base_fs)); } virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { - return fsFsGetFreeSpace(this->base_fs.get(), path, out); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsGetFreeSpace(std::addressof(this->base_fs), sf_path.str, out); } virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) { - return fsFsGetTotalSpace(this->base_fs.get(), path, out); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsGetTotalSpace(std::addressof(this->base_fs), sf_path.str, out); } virtual Result CleanDirectoryRecursivelyImpl(const char *path) { - return fsFsCleanDirectoryRecursively(this->base_fs.get(), path); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsCleanDirectoryRecursively(std::addressof(this->base_fs), sf_path.str); } virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) { + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw)); - return fsFsGetFileTimeStampRaw(this->base_fs.get(), path, reinterpret_cast<::FsTimeStampRaw *>(out)); + return fsFsGetFileTimeStampRaw(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsTimeStampRaw *>(out)); } virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) { - return fsFsQueryEntry(this->base_fs.get(), dst, dst_size, src, src_size, path, static_cast(query)); + fssrv::sf::Path sf_path; + R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); + return fsFsQueryEntry(std::addressof(this->base_fs), dst, dst_size, src, src_size, sf_path.str, static_cast(query)); } }; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp index d90e52fe1..554dcdfef 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp @@ -16,17 +16,17 @@ #pragma once #include "fs_common.hpp" #include "fs_istorage.hpp" +#include "impl/fs_newable.hpp" namespace ams::fs { - class RemoteStorage : public IStorage { + class RemoteStorage : public IStorage, public impl::Newable { private: - std::unique_ptr<::FsStorage> base_storage; + std::unique_ptr<::FsStorage, impl::Deleter> base_storage; public: - RemoteStorage(::FsStorage *s) : base_storage(s) { /* ... */ } - RemoteStorage(std::unique_ptr<::FsStorage> s) : base_storage(std::move(s)) { /* ... */ } - RemoteStorage(::FsStorage s) { - this->base_storage = std::make_unique<::FsStorage>(s); + RemoteStorage(::FsStorage &s) { + this->base_storage = impl::MakeUnique<::FsStorage>(); + *this->base_storage = s; } virtual ~RemoteStorage() { fsStorageClose(this->base_storage.get()); } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp new file mode 100644 index 000000000..6c81d83bb --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs { + + union RightsId { + u8 data[0x10]; + u64 data64[2]; + }; + static_assert(sizeof(RightsId) == 0x10); + static_assert(std::is_pod::value); + + /* Rights ID API */ + Result GetRightsId(RightsId *out, const char *path); + Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path); + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_romfs_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_romfs_filesystem.hpp new file mode 100644 index 000000000..36ae47186 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_romfs_filesystem.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "impl/fs_newable.hpp" +#include "fsa/fs_ifile.hpp" +#include "fsa/fs_idirectory.hpp" +#include "fsa/fs_ifilesystem.hpp" +#include "fs_dbm_hierarchical_rom_file_table.hpp" + +namespace ams::fs { + + class RomFsFileSystem : public fsa::IFileSystem, public impl::Newable { + NON_COPYABLE(RomFsFileSystem); + public: + using RomFileTable = HierarchicalRomFileTable; + private: + RomFileTable rom_file_table; + IStorage *base_storage; + std::unique_ptr unique_storage; + std::unique_ptr dir_bucket_storage; + std::unique_ptr dir_entry_storage; + std::unique_ptr file_bucket_storage; + std::unique_ptr file_entry_storage; + s64 entry_size; + private: + Result GetFileInfo(RomFileTable::FileInfo *out, const char *path); + public: + static Result GetRequiredWorkingMemorySize(size_t *out, IStorage *storage); + public: + RomFsFileSystem(); + virtual ~RomFsFileSystem() override; + + Result Initialize(IStorage *base, void *work, size_t work_size, bool use_cache); + Result Initialize(std::unique_ptr&& base, void *work, size_t work_size, bool use_cache); + + IStorage *GetBaseStorage(); + RomFileTable *GetRomFileTable(); + Result GetFileBaseOffset(s64 *out, const char *path); + public: + virtual Result CreateFileImpl(const char *path, s64 size, int flags) override; + virtual Result DeleteFileImpl(const char *path) override; + virtual Result CreateDirectoryImpl(const char *path) override; + virtual Result DeleteDirectoryImpl(const char *path) override; + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override; + virtual Result RenameFileImpl(const char *old_path, const char *new_path) override; + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override; + virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override; + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) override; + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, fs::OpenDirectoryMode mode) override; + virtual Result CommitImpl() override; + virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override; + virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override; + virtual Result CleanDirectoryRecursivelyImpl(const char *path) override; + + /* These aren't accessible as commands. */ + virtual Result CommitProvisionallyImpl(s64 counter) override; + virtual Result RollbackImpl() override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp new file mode 100644 index 000000000..a6dbbd8ce --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "fs_save_data_types.hpp" + +namespace ams::fs { + + Result DeleteSaveData(SaveDataId id); + Result DeleteSaveData(SaveDataSpaceId space_id, SaveDataId id); + + Result GetSaveDataFlags(u32 *out, SaveDataId id); + Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id); + Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_transaction.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_transaction.hpp new file mode 100644 index 000000000..a39762441 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_transaction.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "fs_save_data_types.hpp" + +namespace ams::fs { + + Result CommitSaveData(const char *path); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp new file mode 100644 index 000000000..8d49efe3f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + using SaveDataId = u64; + using SystemSaveDataId = u64; + using SystemBcatSaveDataId = SystemSaveDataId; + + enum class SaveDataSpaceId : u8 { + System = 0, + User = 1, + SdSystem = 2, + Temporary = 3, + SdUser = 4, + + ProperSystem = 100, + SafeMode = 101, + }; + + enum class SaveDataType : u8 { + System = 0, + Account = 1, + Bcat = 2, + Device = 3, + Temporary = 4, + Cache = 5, + SystemBcat = 6, + }; + + enum class SaveDataRank : u8 { + Primary = 0, + Secondary = 1, + }; + + struct UserId { + u64 data[2]; + }; + static_assert(std::is_pod::value); + + constexpr inline bool operator<(const UserId &lhs, const UserId &rhs) { + if (lhs.data[0] < rhs.data[0]) { + return true; + } else if (lhs.data[0] == rhs.data[0] && lhs.data[1] < rhs.data[1]) { + return true; + } else { + return false; + } + } + + constexpr inline bool operator==(const UserId &lhs, const UserId &rhs) { + return lhs.data[0] == rhs.data[0] && lhs.data[1] == rhs.data[1]; + } + + constexpr inline bool operator!=(const UserId &lhs, const UserId &rhs) { + return !(lhs == rhs); + } + + constexpr inline SystemSaveDataId InvalidSystemSaveDataId = 0; + constexpr inline UserId InvalidUserId = {}; + + enum SaveDataFlags : u32 { + SaveDataFlags_None = (0 << 0), + SaveDataFlags_KeepAfterResettingSystemSaveData = (1 << 0), + SaveDataFlags_KeepAfterRefurbishment = (1 << 1), + SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2), + SaveDataFlags_NeedsSecureDelete = (1 << 3), + }; + + struct SaveDataCreationInfo { + s64 size; + s64 journal_size; + s64 block_size; + u64 owner_id; + u32 flags; + SaveDataSpaceId space_id; + bool pseudo; + u8 reserved[0x1A]; + }; + static_assert(std::is_pod::value); + static_assert(sizeof(SaveDataCreationInfo) == 0x40); + + struct SaveDataAttribute { + ncm::ProgramId program_id; + UserId user_id; + SystemSaveDataId system_save_data_id; + SaveDataType type; + SaveDataRank rank; + u16 index; + u8 reserved[0x1C]; + + static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index, SaveDataRank rank) { + return { + .program_id = program_id, + .user_id = user_id, + .system_save_data_id = system_save_data_id, + .type = type, + .rank = rank, + .index = index, + }; + } + + static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index) { + return Make(program_id, type, user_id, system_save_data_id, index, SaveDataRank::Primary); + } + + static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id) { + return Make(program_id, type, user_id, system_save_data_id, 0, SaveDataRank::Primary); + } + }; + static_assert(sizeof(SaveDataAttribute) == 0x40); + static_assert(std::is_trivially_destructible::value); + + constexpr inline bool operator<(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) { + return std::tie(lhs.program_id, lhs.user_id, lhs.system_save_data_id, lhs.index, lhs.rank) < + std::tie(rhs.program_id, rhs.user_id, rhs.system_save_data_id, rhs.index, rhs.rank); + } + + constexpr inline bool operator==(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) { + return std::tie(lhs.program_id, lhs.user_id, lhs.system_save_data_id, lhs.type, lhs.rank, lhs.index) == + std::tie(rhs.program_id, rhs.user_id, rhs.system_save_data_id, rhs.type, rhs.rank, rhs.index); + } + + constexpr inline bool operator!=(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) { + return !(lhs == rhs); + } + + constexpr inline size_t DefaultSaveDataBlockSize = 16_KB; + + struct SaveDataExtraData { + SaveDataAttribute attr; + u64 owner_id; + s64 timestamp; + u32 flags; + u8 pad[4]; + s64 available_size; + s64 journal_size; + s64 commit_id; + u8 unused[0x190]; + }; + static_assert(sizeof(SaveDataExtraData) == 0x200); + static_assert(std::is_pod::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp new file mode 100644 index 000000000..981620719 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + Result MountSdCard(const char *name); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_signed_system_partition.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_signed_system_partition.hpp new file mode 100644 index 000000000..1c0ff5b28 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_signed_system_partition.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + bool IsSignedSystemPartitionOnSdCardValid(const char *system_root_path); + bool IsSignedSystemPartitionOnSdCardValidDeprecated(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp new file mode 100644 index 000000000..9470b4747 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "impl/fs_newable.hpp" +#include "fs_istorage.hpp" + +namespace ams::fs { + + class SubStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + private: + std::shared_ptr shared_base_storage; + fs::IStorage *base_storage; + s64 offset; + s64 size; + bool resizable; + private: + constexpr bool IsValid() const { + return this->base_storage != nullptr; + } + public: + SubStorage() : shared_base_storage(), base_storage(nullptr), offset(0), size(0), resizable(false) { /* ... */ } + + SubStorage(const SubStorage &rhs) : shared_base_storage(), base_storage(rhs.base_storage), offset(rhs.offset), size(rhs.size), resizable(rhs.resizable) { /* ... */} + SubStorage &operator=(const SubStorage &rhs) { + if (this != std::addressof(rhs)) { + this->base_storage = rhs.base_storage; + this->offset = rhs.offset; + this->size = rhs.size; + this->resizable = rhs.resizable; + } + return *this; + } + + SubStorage(IStorage *storage, s64 o, s64 sz) : shared_base_storage(), base_storage(storage), offset(o), size(sz) { + AMS_ABORT_UNLESS(this->IsValid()); + AMS_ABORT_UNLESS(this->offset >= 0); + AMS_ABORT_UNLESS(this->size >= 0); + } + + SubStorage(std::shared_ptr storage, s64 o, s64 sz) : shared_base_storage(storage), base_storage(storage.get()), offset(o), size(sz) { + AMS_ABORT_UNLESS(this->IsValid()); + AMS_ABORT_UNLESS(this->offset >= 0); + AMS_ABORT_UNLESS(this->size >= 0); + } + + SubStorage(SubStorage *sub, s64 o, s64 sz) : shared_base_storage(), base_storage(sub->base_storage), offset(o + sub->offset), size(sz) { + AMS_ABORT_UNLESS(this->IsValid()); + AMS_ABORT_UNLESS(this->offset >= 0); + AMS_ABORT_UNLESS(this->size >= 0); + AMS_ABORT_UNLESS(sub->size >= o + sz); + } + + public: + void SetResizable(bool rsz) { + this->resizable = rsz; + } + public: + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Ensure we're initialized. */ + R_UNLESS(this->IsValid(), fs::ResultNotInitialized()); + + /* Succeed immediately on zero-sized operation. */ + R_SUCCEED_IF(size == 0); + + + /* Validate arguments and read. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); + return this->base_storage->Read(this->offset + offset, buffer, size); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override{ + /* Ensure we're initialized. */ + R_UNLESS(this->IsValid(), fs::ResultNotInitialized()); + + /* Succeed immediately on zero-sized operation. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments and write. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); + return this->base_storage->Write(this->offset + offset, buffer, size); + } + + virtual Result Flush() override { + R_UNLESS(this->IsValid(), fs::ResultNotInitialized()); + return this->base_storage->Flush(); + } + + virtual Result SetSize(s64 size) override { + /* Ensure we're initialized and validate arguments. */ + R_UNLESS(this->IsValid(), fs::ResultNotInitialized()); + R_UNLESS(this->resizable, fs::ResultUnsupportedOperationInSubStorageA()); + R_UNLESS(IStorage::IsOffsetAndSizeValid(this->offset, size), fs::ResultInvalidSize()); + + /* Ensure that we're allowed to set size. */ + s64 cur_size; + R_TRY(this->base_storage->GetSize(std::addressof(cur_size))); + R_UNLESS(cur_size == this->offset + this->size, fs::ResultUnsupportedOperationInSubStorageB()); + + /* Set the size. */ + R_TRY(this->base_storage->SetSize(this->offset + size)); + + this->size = size; + return ResultSuccess(); + } + + virtual Result GetSize(s64 *out) override { + /* Ensure we're initialized. */ + R_UNLESS(this->IsValid(), fs::ResultNotInitialized()); + + *out = this->size; + return ResultSuccess(); + } + + virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* Ensure we're initialized. */ + R_UNLESS(this->IsValid(), fs::ResultNotInitialized()); + + /* Succeed immediately on zero-sized operation. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments and operate. */ + R_UNLESS(IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); + return this->base_storage->OperateRange(dst, dst_size, op_id, this->offset + offset, size, src, src_size); + } + + using IStorage::OperateRange; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_system_data.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_system_data.hpp new file mode 100644 index 000000000..e95425e86 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_system_data.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + Result QueryMountSystemDataCacheSize(size_t *out, ncm::SystemDataId data_id); + + Result MountSystemData(const char *name, ncm::SystemDataId data_id); + Result MountSystemData(const char *name, ncm::SystemDataId data_id, void *cache_buffer, size_t cache_size); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp new file mode 100644 index 000000000..95093dcde --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "fs_save_data_types.hpp" + +namespace ams::fs { + + void DisableAutoSaveDataCreation(); + + Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags); + + Result MountSystemSaveData(const char *name, SystemSaveDataId id); + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id); + + Result MountSystemSaveData(const char *name, SystemSaveDataId id, UserId user_id); + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id); + + Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp index 0d474604e..5466dfe80 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp @@ -96,10 +96,12 @@ namespace ams::fs::fsa { return ResultSuccess(); } - Result DrySetSize(s64 size, OpenMode open_mode) { + Result DrySetSize(s64 size, fs::OpenMode open_mode) { /* Check that we can write. */ R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultInvalidOperationForOpenMode()); + AMS_ASSERT(size >= 0); + return ResultSuccess(); } private: diff --git a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp index cc897523e..f785b18fe 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp @@ -24,7 +24,8 @@ namespace ams::fs::fsa { class IDirectory; enum class QueryId { - SetConcatenationFileAttribute = FsFileSystemQueryId_SetConcatenationFileAttribute + SetConcatenationFileAttribute = 0, + IsSignedSystemPartitionOnSdCardValid = 2, }; class IFileSystem { @@ -88,10 +89,10 @@ namespace ams::fs::fsa { } Result OpenDirectory(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) { - R_UNLESS(path != nullptr, fs::ResultInvalidPath()); - R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument()); - R_UNLESS((mode & OpenDirectoryMode_All) != 0, fs::ResultInvalidArgument()); - R_UNLESS((mode & ~OpenDirectoryMode_All) == 0, fs::ResultInvalidArgument()); + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument()); + R_UNLESS((mode & OpenDirectoryMode_All) != 0, fs::ResultInvalidArgument()); + R_UNLESS((mode & ~(OpenDirectoryMode_All | OpenDirectoryMode_NotRequireFileSize)) == 0, fs::ResultInvalidArgument()); return this->OpenDirectoryImpl(out_dir, path, mode); } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp new file mode 100644 index 000000000..36d283407 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fsa/fs_registrar.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "../fs_common.hpp" + +namespace ams::fs::fsa { + + class ICommonMountNameGenerator { + public: + virtual ~ICommonMountNameGenerator() { /* ... */ } + virtual Result GenerateCommonMountName(char *name, size_t name_size) = 0; + }; + + class IFileSystem; + + Result Register(const char *name, std::unique_ptr &&fs); + Result Register(const char *name, std::unique_ptr &&fs, std::unique_ptr &&generator); + Result Register(const char *name, std::unique_ptr &&fs, std::unique_ptr &&generator, bool use_data_cache, bool use_path_cache, bool multi_commit_supported); + + void Unregister(const char *name); +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp new file mode 100644 index 000000000..4e743762f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +namespace ams::fs::impl { + + /* Delimiting of mount names. */ + constexpr inline const char ReservedMountNamePrefixCharacter = '@'; + constexpr inline const char * const MountNameDelimiter = ":/"; + + /* Filesystem names. */ + constexpr inline const char * const HostRootFileSystemMountName = "@Host"; + constexpr inline const char * const SdCardFileSystemMountName = "@Sdcard"; + constexpr inline const char * const GameCardFileSystemMountName = "@Gc"; + + constexpr inline size_t GameCardFileSystemMountNameSuffixLength = 1; + + constexpr inline const char * const GameCardFileSystemMountNameUpdateSuffix = "U"; + constexpr inline const char * const GameCardFileSystemMountNameNormalSuffix = "N"; + constexpr inline const char * const GameCardFileSystemMountNameSecureSuffix = "S"; + + /* Built-in storage names. */ + constexpr inline const char * const BisCalibrationFilePartitionMountName = "@CalibFile"; + constexpr inline const char * const BisSafeModePartitionMountName = "@Safe"; + constexpr inline const char * const BisUserPartitionMountName = "@User"; + constexpr inline const char * const BisSystemPartitionMountName = "@System"; + + /* Content storage names. */ + constexpr inline const char * const ContentStorageSystemMountName = "@SystemContent"; + constexpr inline const char * const ContentStorageUserMountName = "@UserContent"; + constexpr inline const char * const ContentStorageSdCardMountName = "@SdCardContent"; + + + /* Registered update partition. */ + constexpr inline const char * const RegisteredUpdatePartitionMountName = "@RegUpdate"; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_data.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_data.hpp new file mode 100644 index 000000000..4db782114 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_data.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + Result QueryMountDataCacheSize(size_t *out, ncm::DataId data_id, ncm::StorageId storage_id); + + Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id); + Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size); + Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_data_cache, bool use_path_cache); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_filesystem_proxy_type.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_filesystem_proxy_type.hpp new file mode 100644 index 000000000..46f36d41d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_filesystem_proxy_type.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + enum FileSystemProxyType { + FileSystemProxyType_Code = 0, + FileSystemProxyType_Rom = 1, + FileSystemProxyType_Logo = 2, + FileSystemProxyType_Control = 3, + FileSystemProxyType_Manual = 4, + FileSystemProxyType_Meta = 5, + FileSystemProxyType_Data = 6, + FileSystemProxyType_Package = 7, + FileSystemProxyType_UpdatePartition = 8, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp new file mode 100644 index 000000000..7ceb7717f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + class Newable { + public: + static void *operator new(size_t size) { + return ::ams::fs::impl::Allocate(size); + } + + static void *operator new[](size_t size) { + return ::ams::fs::impl::Allocate(size); + } + + static void operator delete(void *ptr, size_t size) { + return ::ams::fs::impl::Deallocate(ptr, size); + } + + static void operator delete[](void *ptr, size_t size) { + return ::ams::fs::impl::Deallocate(ptr, size); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp index e2bcb1304..a847a258d 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp @@ -14,9 +14,9 @@ * along with this program. If not, see . */ #pragma once -#include "../fs/fs_common.hpp" -#include "../fs/fs_directory.hpp" -#include "../sf/sf_buffer_tags.hpp" +#include +#include +#include namespace ams::fssrv::sf { diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_archive.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_archive.hpp index 32d164090..60596272c 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_archive.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_archive.hpp @@ -13,9 +13,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once -#include "kvdb_auto_buffer.hpp" +#include namespace ams::kvdb { diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp index bb5378b70..db98774bf 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once #include @@ -39,7 +38,7 @@ namespace ams::kvdb { } AutoBuffer& operator=(AutoBuffer &&rhs) { - rhs.Swap(*this); + AutoBuffer(std::move(rhs)).Swap(*this); return *this; } @@ -70,9 +69,8 @@ namespace ams::kvdb { /* Allocate a buffer. */ this->buffer = static_cast(std::malloc(size)); - if (this->buffer == nullptr) { - return ResultAllocationFailed(); - } + R_UNLESS(this->buffer != nullptr, ResultAllocationFailed()); + this->size = size; return ResultSuccess(); } diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp index 7d0aec0b0..0c85ddc76 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_bounded_string.hpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once #include @@ -51,6 +50,7 @@ namespace ams::kvdb { std::va_list args; va_start(args, format); CheckLength(std::vsnprintf(string.buffer, N, format, args)); + string.buffer[N - 1] = 0; va_end(args); return string; @@ -74,6 +74,7 @@ namespace ams::kvdb { /* Ensure string can fit in our buffer. */ CheckLength(strnlen(s, N)); std::strncpy(this->buffer, s, N); + this->buffer[N - 1] = 0; } void SetFormat(const char *format, ...) __attribute__((format (printf, 2, 3))) { diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp index c98c25188..52f8b2708 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp @@ -13,9 +13,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include -#include "kvdb_bounded_string.hpp" -#include "kvdb_file_key_value_store.hpp" +#include +#include +#include +#include namespace ams::kvdb { @@ -39,16 +40,16 @@ namespace ams::kvdb { public: static Result CreateNewList(const char *path) { /* Create new lru_list.dat. */ - R_TRY(fsdevCreateFile(path, FileSize, 0)); + R_TRY(fs::CreateFile(path, FileSize)); /* Open the file. */ - FILE *fp = fopen(path, "r+b"); - R_UNLESS(fp != nullptr, fsdevGetLastResult()); - ON_SCOPE_EXIT { fclose(fp); }; + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Write)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; /* Write new header with zero entries to the file. */ LruHeader new_header = { .entry_count = 0, }; - R_UNLESS(fwrite(&new_header, sizeof(new_header), 1, fp) == 1, fsdevGetLastResult()); + R_TRY(fs::WriteFile(file, 0, std::addressof(new_header), sizeof(new_header), fs::WriteOption::Flush)); return ResultSuccess(); } @@ -80,36 +81,33 @@ namespace ams::kvdb { std::memset(this->keys, 0, BufferSize); /* Open file. */ - FILE *fp = fopen(this->file_path, "rb"); - R_UNLESS(fp != nullptr, fsdevGetLastResult()); - ON_SCOPE_EXIT { fclose(fp); }; + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), this->file_path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; /* Read header. */ - R_UNLESS(fread(&this->header, sizeof(this->header), 1, fp) == 1, fsdevGetLastResult()); + R_TRY(fs::ReadFile(file, 0, std::addressof(this->header), sizeof(this->header))); /* Read entries. */ - const size_t count = this->GetCount(); - if (count > 0) { - R_UNLESS(fread(this->keys, std::min(BufferSize, sizeof(Key) * count), 1, fp) == 1, fsdevGetLastResult()); - } + R_TRY(fs::ReadFile(file, sizeof(this->header), this->keys, BufferSize)); return ResultSuccess(); } Result Save() { /* Open file. */ - FILE *fp = fopen(this->file_path, "r+b"); - R_UNLESS(fp != nullptr, fsdevGetLastResult()); - ON_SCOPE_EXIT { fclose(fp); }; + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), this->file_path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; /* Write header. */ - R_UNLESS(fwrite(&this->header, sizeof(this->header), 1, fp) == 1, fsdevGetLastResult()); + R_TRY(fs::WriteFile(file, 0, std::addressof(this->header), sizeof(this->header), fs::WriteOption::None)); /* Write entries. */ - R_UNLESS(fwrite(this->keys, BufferSize, 1, fp) == 1, fsdevGetLastResult()); + R_TRY(fs::WriteFile(file, sizeof(this->header), this->keys, BufferSize, fs::WriteOption::None)); /* Flush. */ - fflush(fp); + R_TRY(fs::FlushFile(file)); return ResultSuccess(); } @@ -209,44 +207,37 @@ namespace ams::kvdb { return Path::MakeFormat("%s/%s", dir, "kvs"); } - static Result Exists(bool *out, const char *path, bool is_dir) { + static Result Exists(bool *out, const char *path, fs::DirectoryEntryType type) { /* Set out to false initially. */ *out = false; - /* Check that the path exists, and that our entry type is correct. */ - { - struct stat st; + /* Try to get the entry type. */ + fs::DirectoryEntryType entry_type; + R_TRY_CATCH(fs::GetEntryType(std::addressof(entry_type), path)) { + /* If the path doesn't exist, nothing has gone wrong. */ + R_CONVERT(fs::ResultPathNotFound, ResultSuccess()); + } R_END_TRY_CATCH; - if (stat(path, &st) != 0) { - R_TRY_CATCH(fsdevGetLastResult()) { - /* If the path doesn't exist, nothing has gone wrong. */ - R_CONVERT(fs::ResultPathNotFound, ResultSuccess()); - } R_END_TRY_CATCH; - } - - if (is_dir) { - R_UNLESS((S_ISDIR(st.st_mode)), ResultInvalidFilesystemState()); - } else { - R_UNLESS((S_ISREG(st.st_mode)), ResultInvalidFilesystemState()); - } - } + /* Check that the entry type is correct. */ + R_UNLESS(entry_type == type, ResultInvalidFilesystemState()); + /* The entry exists and is the correct type. */ *out = true; return ResultSuccess(); } static Result DirectoryExists(bool *out, const char *path) { - return Exists(out, path, true); + return Exists(out, path, fs::DirectoryEntryType_Directory); } static Result FileExists(bool *out, const char *path) { - return Exists(out, path, false); + return Exists(out, path, fs::DirectoryEntryType_File); } public: static Result CreateNewCache(const char *dir) { /* Make a new key value store filesystem, and a new lru_list.dat. */ R_TRY(LeastRecentlyUsedList::CreateNewList(GetLeastRecentlyUsedListPath(dir))); - R_UNLESS(mkdir(GetFileKeyValueStorePath(dir), 0) == 0, fsdevGetLastResult()); + R_TRY(fs::CreateDirectory(dir)); return ResultSuccess(); } diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp index e772897ec..40e5fbc84 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp @@ -14,8 +14,8 @@ * along with this program. If not, see . */ #pragma once -#include "../os.hpp" -#include "kvdb_bounded_string.hpp" +#include +#include namespace ams::kvdb { diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp index 00a10dfe0..65ca787ed 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp @@ -15,10 +15,11 @@ */ #pragma once -#include -#include "kvdb_auto_buffer.hpp" -#include "kvdb_archive.hpp" -#include "kvdb_bounded_string.hpp" +#include +#include +#include +#include +#include namespace ams::kvdb { @@ -262,11 +263,9 @@ namespace ams::kvdb { Result Initialize(const char *dir, size_t capacity) { /* Ensure that the passed path is a directory. */ - { - struct stat st; - R_UNLESS(stat(dir, &st) == 0, fs::ResultPathNotFound()); - R_UNLESS((S_ISDIR(st.st_mode)), fs::ResultPathNotFound()); - } + fs::DirectoryEntryType entry_type; + R_TRY(fs::GetEntryType(std::addressof(entry_type), dir)); + R_UNLESS(entry_type == fs::DirectoryEntryType_Directory, fs::ResultPathNotFound()); /* Set paths. */ this->path.SetFormat("%s%s", dir, "/imkvdb.arc"); @@ -337,7 +336,7 @@ namespace ams::kvdb { return ResultSuccess(); } - Result Save() { + Result Save(bool destructive = false) { /* Create a buffer to hold the archive. */ AutoBuffer buffer; R_TRY(buffer.Initialize(this->GetArchiveSize())); @@ -353,7 +352,7 @@ namespace ams::kvdb { } /* Save the buffer to disk. */ - return this->Commit(buffer); + return this->Commit(buffer, destructive); } Result Set(const Key &key, const void *value, size_t value_size) { @@ -468,27 +467,38 @@ namespace ams::kvdb { return this->index.find(key); } private: - Result Commit(const AutoBuffer &buffer) { - /* Try to delete temporary archive, but allow deletion failure (it may not exist). */ - std::remove(this->temp_path.Get()); + Result SaveArchiveToFile(const char *path, const void *buf, size_t size) { + /* Try to delete the archive, but allow deletion failure. */ + fs::DeleteFile(path); - /* Create new temporary archive. */ - R_TRY(fsdevCreateFile(this->temp_path.Get(), buffer.GetSize(), 0)); + /* Create new archive. */ + R_TRY(fs::CreateFile(path, size)); - /* Write data to the temporary archive. */ + /* Write data to the archive. */ { - FILE *f = fopen(this->temp_path, "r+b"); - R_UNLESS(f != nullptr, fsdevGetLastResult()); - ON_SCOPE_EXIT { fclose(f); }; - - R_UNLESS(fwrite(buffer.Get(), buffer.GetSize(), 1, f) == 1, fsdevGetLastResult()); + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Write)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + R_TRY(fs::WriteFile(file, 0, buf, size, fs::WriteOption::Flush)); } - /* Try to delete the saved archive, but allow deletion failure. */ - std::remove(this->path.Get()); + return ResultSuccess(); + } - /* Rename the path. */ - R_UNLESS(std::rename(this->temp_path.Get(), this->path.Get()) == 0, fsdevGetLastResult()); + Result Commit(const AutoBuffer &buffer, bool destructive) { + if (destructive) { + /* Delete and save to the real archive. */ + R_TRY(SaveArchiveToFile(this->path.Get(), buffer.Get(), buffer.GetSize())); + } else { + /* Delete and save to a temporary archive. */ + R_TRY(SaveArchiveToFile(this->temp_path.Get(), buffer.Get(), buffer.GetSize())); + + /* Try to delete the saved archive, but allow deletion failure. */ + fs::DeleteFile(this->path.Get()); + + /* Rename the path. */ + R_TRY(fs::RenameFile(this->temp_path.Get(), this->path.Get())); + } return ResultSuccess(); } @@ -505,18 +515,17 @@ namespace ams::kvdb { Result ReadArchiveFile(AutoBuffer *dst) const { /* Open the file. */ - FILE *f = fopen(this->path, "rb"); - R_UNLESS(f != nullptr, fsdevGetLastResult()); - ON_SCOPE_EXIT { fclose(f); }; + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; /* Get the archive file size. */ - fseek(f, 0, SEEK_END); - const size_t archive_size = ftell(f); - fseek(f, 0, SEEK_SET); + s64 archive_size; + R_TRY(fs::GetFileSize(std::addressof(archive_size), file)); /* Make a new buffer, read the file. */ - R_TRY(dst->Initialize(archive_size)); - R_UNLESS(fread(dst->Get(), archive_size, 1, f) == 1, fsdevGetLastResult()); + R_TRY(dst->Initialize(static_cast(archive_size))); + R_TRY(fs::ReadFile(file, 0, dst->Get(), dst->GetSize())); return ResultSuccess(); } diff --git a/libraries/libstratosphere/include/stratosphere/ldr/ldr_pm_api.hpp b/libraries/libstratosphere/include/stratosphere/ldr/ldr_pm_api.hpp index c680e07bc..1b1947557 100644 --- a/libraries/libstratosphere/include/stratosphere/ldr/ldr_pm_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/ldr/ldr_pm_api.hpp @@ -15,7 +15,7 @@ */ #pragma once -#include "ldr_types.hpp" +#include namespace ams::ldr::pm { diff --git a/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp b/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp index a2ecfdd35..462037f40 100644 --- a/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp @@ -16,8 +16,9 @@ #pragma once #include -#include "../ncm/ncm_types.hpp" -#include "../sf/sf_buffer_tags.hpp" +#include +#include +#include namespace ams::ldr { diff --git a/libraries/libstratosphere/include/stratosphere/lr.hpp b/libraries/libstratosphere/include/stratosphere/lr.hpp new file mode 100644 index 000000000..2c66e5dc4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_add_on_content_location_resolver.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_add_on_content_location_resolver.hpp new file mode 100644 index 000000000..c204898ba --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_add_on_content_location_resolver.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::lr { + + class AddOnContentLocationResolver { + NON_COPYABLE(AddOnContentLocationResolver); + private: + std::shared_ptr interface; + public: + AddOnContentLocationResolver() { /* ... */ } + explicit AddOnContentLocationResolver(std::shared_ptr intf) : interface(std::move(intf)) { /* ... */ } + + AddOnContentLocationResolver(AddOnContentLocationResolver &&rhs) { + this->interface = std::move(rhs.interface); + } + + AddOnContentLocationResolver &operator=(AddOnContentLocationResolver &&rhs) { + AddOnContentLocationResolver(std::move(rhs)).Swap(*this); + return *this; + } + + void Swap(AddOnContentLocationResolver &rhs) { + std::swap(this->interface, rhs.interface); + } + public: + /* Actual commands. */ + Result ResolveAddOnContentPath(Path *out, ncm::DataId id) { + AMS_ASSERT(this->interface); + return this->interface->ResolveAddOnContentPath(out, id); + } + + Result RegisterAddOnContentStorage(ncm::DataId id, ncm::ApplicationId application_id, ncm::StorageId storage_id) { + AMS_ASSERT(this->interface); + if (hos::GetVersion() >= hos::Version_900) { + return this->interface->RegisterAddOnContentStorage(id, application_id, storage_id); + } else { + return this->interface->RegisterAddOnContentStorageDeprecated(id, storage_id); + } + } + + Result UnregisterAllAddOnContentPath() { + AMS_ASSERT(this->interface); + return this->interface->UnregisterAllAddOnContentPath(); + } + + Result RefreshApplicationAddOnContent(const ncm::ApplicationId *ids, size_t num_ids) { + AMS_ASSERT(this->interface); + return this->interface->RefreshApplicationAddOnContent(sf::InArray(ids, num_ids)); + } + + Result UnregisterApplicationAddOnContent(ncm::ApplicationId id) { + AMS_ASSERT(this->interface); + return this->interface->UnregisterApplicationAddOnContent(id); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_api.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_api.hpp new file mode 100644 index 000000000..41db1e5a3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_api.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace ams::lr { + + /* Management. */ + void Initialize(); + void Finalize(); + + /* Service API. */ + Result OpenLocationResolver(LocationResolver *out, ncm::StorageId storage_id); + Result OpenRegisteredLocationResolver(RegisteredLocationResolver *out); + Result OpenAddOnContentLocationResolver(AddOnContentLocationResolver *out); + Result RefreshLocationResolver(ncm::StorageId storage_id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_i_add_on_content_location_resolver.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_i_add_on_content_location_resolver.hpp new file mode 100644 index 000000000..17080961c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_i_add_on_content_location_resolver.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::lr { + + class IAddOnContentLocationResolver : public sf::IServiceObject { + protected: + enum class CommandId { + ResolveAddOnContentPath = 0, + RegisterAddOnContentStorageDeprecated = 1, + RegisterAddOnContentStorage = 1, + UnregisterAllAddOnContentPath = 2, + RefreshApplicationAddOnContent = 3, + UnregisterApplicationAddOnContent = 4, + }; + public: + /* Actual commands. */ + virtual Result ResolveAddOnContentPath(sf::Out out, ncm::DataId id) = 0; + virtual Result RegisterAddOnContentStorageDeprecated(ncm::DataId id, ncm::StorageId storage_id) = 0; + virtual Result RegisterAddOnContentStorage(ncm::DataId id, ncm::ApplicationId application_id, ncm::StorageId storage_id) = 0; + virtual Result UnregisterAllAddOnContentPath() = 0; + virtual Result RefreshApplicationAddOnContent(const sf::InArray &ids) = 0; + virtual Result UnregisterApplicationAddOnContent(ncm::ApplicationId id) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(ResolveAddOnContentPath, hos::Version_200), + MAKE_SERVICE_COMMAND_META(RegisterAddOnContentStorageDeprecated, hos::Version_200, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RegisterAddOnContentStorage, hos::Version_900), + MAKE_SERVICE_COMMAND_META(UnregisterAllAddOnContentPath, hos::Version_200), + MAKE_SERVICE_COMMAND_META(RefreshApplicationAddOnContent, hos::Version_900), + MAKE_SERVICE_COMMAND_META(UnregisterApplicationAddOnContent, hos::Version_900), + }; + }; + + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver.hpp new file mode 100644 index 000000000..4e764ad2b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver.hpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::lr { + + class ILocationResolver : public sf::IServiceObject { + NON_COPYABLE(ILocationResolver); + NON_MOVEABLE(ILocationResolver); + protected: + enum class CommandId { + ResolveProgramPath = 0, + RedirectProgramPath = 1, + ResolveApplicationControlPath = 2, + ResolveApplicationHtmlDocumentPath = 3, + ResolveDataPath = 4, + RedirectApplicationControlPathDeprecated = 5, + RedirectApplicationControlPath = 5, + RedirectApplicationHtmlDocumentPathDeprecated = 6, + RedirectApplicationHtmlDocumentPath = 6, + ResolveApplicationLegalInformationPath = 7, + RedirectApplicationLegalInformationPathDeprecated = 8, + RedirectApplicationLegalInformationPath = 8, + Refresh = 9, + RedirectApplicationProgramPathDeprecated = 10, + RedirectApplicationProgramPath = 10, + ClearApplicationRedirectionDeprecated = 11, + ClearApplicationRedirection = 11, + EraseProgramRedirection = 12, + EraseApplicationControlRedirection = 13, + EraseApplicationHtmlDocumentRedirection = 14, + EraseApplicationLegalInformationRedirection = 15, + ResolveProgramPathForDebug = 16, + RedirectProgramPathForDebug = 17, + RedirectApplicationProgramPathForDebugDeprecated = 18, + RedirectApplicationProgramPathForDebug = 18, + EraseProgramRedirectionForDebug = 19, + }; + public: + ILocationResolver() { /* ... */ } + public: + /* Actual commands. */ + virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) = 0; + virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) = 0; + virtual Result ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) = 0; + virtual Result ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) = 0; + virtual Result ResolveDataPath(sf::Out out, ncm::DataId id) = 0; + virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) = 0; + virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result Refresh() = 0; + virtual Result RedirectApplicationProgramPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectApplicationProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result ClearApplicationRedirectionDeprecated() = 0; + virtual Result ClearApplicationRedirection(const sf::InArray &excluding_ids) = 0; + virtual Result EraseProgramRedirection(ncm::ProgramId id) = 0; + virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) = 0; + virtual Result EraseApplicationHtmlDocumentRedirection(ncm::ProgramId id) = 0; + virtual Result EraseApplicationLegalInformationRedirection(ncm::ProgramId id) = 0; + virtual Result ResolveProgramPathForDebug(sf::Out out, ncm::ProgramId id) = 0; + virtual Result RedirectProgramPathForDebug(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectApplicationProgramPathForDebugDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectApplicationProgramPathForDebug(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result EraseProgramRedirectionForDebug(ncm::ProgramId id) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(ResolveProgramPath), + MAKE_SERVICE_COMMAND_META(RedirectProgramPath), + MAKE_SERVICE_COMMAND_META(ResolveApplicationControlPath), + MAKE_SERVICE_COMMAND_META(ResolveApplicationHtmlDocumentPath), + MAKE_SERVICE_COMMAND_META(ResolveDataPath), + MAKE_SERVICE_COMMAND_META(RedirectApplicationControlPathDeprecated, hos::Version_100, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RedirectApplicationControlPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(RedirectApplicationHtmlDocumentPathDeprecated, hos::Version_100, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RedirectApplicationHtmlDocumentPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(ResolveApplicationLegalInformationPath), + MAKE_SERVICE_COMMAND_META(RedirectApplicationLegalInformationPathDeprecated, hos::Version_100, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RedirectApplicationLegalInformationPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(Refresh), + MAKE_SERVICE_COMMAND_META(RedirectApplicationProgramPathDeprecated, hos::Version_500, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RedirectApplicationProgramPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(ClearApplicationRedirectionDeprecated, hos::Version_500, hos::Version_810), + MAKE_SERVICE_COMMAND_META(ClearApplicationRedirection, hos::Version_900), + MAKE_SERVICE_COMMAND_META(EraseProgramRedirection, hos::Version_500), + MAKE_SERVICE_COMMAND_META(EraseApplicationControlRedirection, hos::Version_500), + MAKE_SERVICE_COMMAND_META(EraseApplicationHtmlDocumentRedirection, hos::Version_500), + MAKE_SERVICE_COMMAND_META(EraseApplicationLegalInformationRedirection, hos::Version_500), + MAKE_SERVICE_COMMAND_META(ResolveProgramPathForDebug, hos::Version_700), + MAKE_SERVICE_COMMAND_META(RedirectProgramPathForDebug, hos::Version_700), + MAKE_SERVICE_COMMAND_META(RedirectApplicationProgramPathForDebugDeprecated, hos::Version_700, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RedirectApplicationProgramPathForDebug, hos::Version_900), + MAKE_SERVICE_COMMAND_META(EraseProgramRedirectionForDebug, hos::Version_700), + }; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver_manager.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver_manager.hpp new file mode 100644 index 000000000..5d9c55184 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_i_location_resolver_manager.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace ams::lr { + + class ILocationResolverManager : public sf::IServiceObject { + protected: + enum class CommandId { + OpenLocationResolver = 0, + OpenRegisteredLocationResolver = 1, + RefreshLocationResolver = 2, + OpenAddOnContentLocationResolver = 3, + }; + public: + /* Actual commands. */ + virtual Result OpenLocationResolver(sf::Out> out, ncm::StorageId storage_id) = 0; + virtual Result OpenRegisteredLocationResolver(sf::Out> out) = 0; + virtual Result RefreshLocationResolver(ncm::StorageId storage_id) = 0; + virtual Result OpenAddOnContentLocationResolver(sf::Out> out) = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_i_registered_location_resolver.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_i_registered_location_resolver.hpp new file mode 100644 index 000000000..255a45f07 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_i_registered_location_resolver.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::lr { + + class IRegisteredLocationResolver : public sf::IServiceObject { + protected: + enum class CommandId { + ResolveProgramPath = 0, + RegisterProgramPathDeprecated = 1, + RegisterProgramPath = 1, + UnregisterProgramPath = 2, + RedirectProgramPathDeprecated = 3, + RedirectProgramPath = 3, + ResolveHtmlDocumentPath = 4, + RegisterHtmlDocumentPathDeprecated = 5, + RegisterHtmlDocumentPath = 5, + UnregisterHtmlDocumentPath = 6, + RedirectHtmlDocumentPathDeprecated = 7, + RedirectHtmlDocumentPath = 7, + Refresh = 8, + RefreshExcluding = 9, + }; + public: + /* Actual commands. */ + virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) = 0; + virtual Result RegisterProgramPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RegisterProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result UnregisterProgramPath(ncm::ProgramId id) = 0; + virtual Result RedirectProgramPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result ResolveHtmlDocumentPath(sf::Out out, ncm::ProgramId id) = 0; + virtual Result RegisterHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RegisterHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result UnregisterHtmlDocumentPath(ncm::ProgramId id) = 0; + virtual Result RedirectHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) = 0; + virtual Result RedirectHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) = 0; + virtual Result Refresh() = 0; + virtual Result RefreshExcluding(const sf::InArray &ids) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(ResolveProgramPath), + MAKE_SERVICE_COMMAND_META(RegisterProgramPathDeprecated, hos::Version_100, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RegisterProgramPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(UnregisterProgramPath), + MAKE_SERVICE_COMMAND_META(RedirectProgramPathDeprecated, hos::Version_100, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RedirectProgramPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(ResolveHtmlDocumentPath, hos::Version_200), + MAKE_SERVICE_COMMAND_META(RegisterHtmlDocumentPathDeprecated, hos::Version_200, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RegisterHtmlDocumentPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(UnregisterHtmlDocumentPath, hos::Version_200), + MAKE_SERVICE_COMMAND_META(RedirectHtmlDocumentPathDeprecated, hos::Version_200, hos::Version_810), + MAKE_SERVICE_COMMAND_META(RedirectHtmlDocumentPath, hos::Version_900), + MAKE_SERVICE_COMMAND_META(Refresh, hos::Version_700), + MAKE_SERVICE_COMMAND_META(RefreshExcluding, hos::Version_900), + }; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver.hpp new file mode 100644 index 000000000..0291d7634 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver.hpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::lr { + + class LocationResolver { + NON_COPYABLE(LocationResolver); + private: + std::shared_ptr interface; + public: + LocationResolver() { /* ... */ } + explicit LocationResolver(std::shared_ptr intf) : interface(std::move(intf)) { /* ... */ } + + LocationResolver(LocationResolver &&rhs) { + this->interface = std::move(rhs.interface); + } + + LocationResolver &operator=(LocationResolver &&rhs) { + LocationResolver(std::move(rhs)).Swap(*this); + return *this; + } + + void Swap(LocationResolver &rhs) { + std::swap(this->interface, rhs.interface); + } + public: + Result ResolveProgramPath(Path *out, ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ResolveProgramPath(out, id); + } + + void RedirectProgramPath(const Path &path, ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + R_ABORT_UNLESS(this->interface->RedirectProgramPath(path, id)); + } + + Result ResolveApplicationControlPath(Path *out, ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ResolveApplicationControlPath(out, id); + } + + Result ResolveApplicationHtmlDocumentPath(Path *out, ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ResolveApplicationHtmlDocumentPath(out, id); + } + + Result ResolveDataPath(Path *out, ncm::DataId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ResolveDataPath(out, id); + } + + void RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface != nullptr); + if (hos::GetVersion() >= hos::Version_900) { + R_ABORT_UNLESS(this->interface->RedirectApplicationControlPath(path, id, owner_id)); + } else { + R_ABORT_UNLESS(this->interface->RedirectApplicationControlPathDeprecated(path, id)); + } + } + + void RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface != nullptr); + if (hos::GetVersion() >= hos::Version_900) { + R_ABORT_UNLESS(this->interface->RedirectApplicationHtmlDocumentPath(path, id, owner_id)); + } else { + R_ABORT_UNLESS(this->interface->RedirectApplicationHtmlDocumentPathDeprecated(path, id)); + } + } + + Result ResolveApplicationLegalInformationPath(Path *out, ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ResolveApplicationLegalInformationPath(out, id); + } + + void RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface != nullptr); + if (hos::GetVersion() >= hos::Version_900) { + R_ABORT_UNLESS(this->interface->RedirectApplicationLegalInformationPath(path, id, owner_id)); + } else { + R_ABORT_UNLESS(this->interface->RedirectApplicationLegalInformationPathDeprecated(path, id)); + } + } + + Result Refresh() { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Refresh(); + } + + void RedirectApplicationProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface != nullptr); + if (hos::GetVersion() >= hos::Version_900) { + R_ABORT_UNLESS(this->interface->RedirectApplicationProgramPath(path, id, owner_id)); + } else { + R_ABORT_UNLESS(this->interface->RedirectApplicationProgramPathDeprecated(path, id)); + } + } + + Result ClearApplicationRedirection() { + AMS_ASSERT(this->interface != nullptr); + AMS_ASSERT(hos::GetVersion() < hos::Version_900); + return this->ClearApplicationRedirection(nullptr, 0); + } + + Result ClearApplicationRedirection(const ncm::ProgramId *excluding_ids, size_t num_ids) { + AMS_ASSERT(this->interface != nullptr); + if (hos::GetVersion() >= hos::Version_900) { + return this->interface->ClearApplicationRedirection(sf::InArray(excluding_ids, num_ids)); + } else { + return this->interface->ClearApplicationRedirectionDeprecated(); + } + } + + Result EraseProgramRedirection(ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->EraseProgramRedirection(id); + } + + Result EraseApplicationControlRedirection(ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->EraseApplicationControlRedirection(id); + } + + Result EraseApplicationHtmlDocumentRedirection(ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->EraseApplicationHtmlDocumentRedirection(id); + } + + Result EraseApplicationLegalInformationRedirection(ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->EraseApplicationLegalInformationRedirection(id); + } + + Result ResolveProgramPathForDebug(Path *out, ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ResolveProgramPathForDebug(out, id); + } + + void RedirectProgramPathForDebug(const Path &path, ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + R_ABORT_UNLESS(this->interface->RedirectProgramPathForDebug(path, id)); + } + + void RedirectApplicationProgramPathForDebug(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface != nullptr); + if (hos::GetVersion() >= hos::Version_900) { + R_ABORT_UNLESS(this->interface->RedirectApplicationProgramPathForDebug(path, id, owner_id)); + } else { + R_ABORT_UNLESS(this->interface->RedirectApplicationProgramPathForDebugDeprecated(path, id)); + } + } + + Result EraseProgramRedirectionForDebug(ncm::ProgramId id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->EraseProgramRedirectionForDebug(id); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp new file mode 100644 index 000000000..daa198e06 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_location_resolver_manager_impl.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::lr { + + class LocationResolverManagerImpl final : public ILocationResolverManager { + private: + /* Resolver storage. */ + ncm::BoundedMap, 5> location_resolvers; + std::shared_ptr registered_location_resolver = nullptr; + std::shared_ptr add_on_content_location_resolver = nullptr; + + os::Mutex mutex; + public: + /* Actual commands. */ + virtual Result OpenLocationResolver(sf::Out> out, ncm::StorageId storage_id) override; + virtual Result OpenRegisteredLocationResolver(sf::Out> out) override; + virtual Result RefreshLocationResolver(ncm::StorageId storage_id) override; + virtual Result OpenAddOnContentLocationResolver(sf::Out> out) override; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(OpenLocationResolver), + MAKE_SERVICE_COMMAND_META(OpenRegisteredLocationResolver), + MAKE_SERVICE_COMMAND_META(RefreshLocationResolver), + MAKE_SERVICE_COMMAND_META(OpenAddOnContentLocationResolver, hos::Version_200), + }; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_registered_location_resolver.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_registered_location_resolver.hpp new file mode 100644 index 000000000..5b26b034e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_registered_location_resolver.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::lr { + + class RegisteredLocationResolver { + NON_COPYABLE(RegisteredLocationResolver); + private: + std::shared_ptr interface; + public: + RegisteredLocationResolver() { /* ... */ } + explicit RegisteredLocationResolver(std::shared_ptr intf) : interface(std::move(intf)) { /* ... */ } + + RegisteredLocationResolver(RegisteredLocationResolver &&rhs) { + this->interface = std::move(rhs.interface); + } + + RegisteredLocationResolver &operator=(RegisteredLocationResolver &&rhs) { + RegisteredLocationResolver(std::move(rhs)).Swap(*this); + return *this; + } + + void Swap(RegisteredLocationResolver &rhs) { + std::swap(this->interface, rhs.interface); + } + public: + /* Actual commands. */ + Result ResolveProgramPath(Path *out, ncm::ProgramId id) { + AMS_ASSERT(this->interface); + return this->interface->ResolveProgramPath(out, id); + } + + Result RegisterProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface); + if (hos::GetVersion() >= hos::Version_900) { + return this->interface->RegisterProgramPath(path, id, owner_id); + } else { + return this->interface->RegisterProgramPathDeprecated(path, id); + } + } + + Result UnregisterProgramPath(ncm::ProgramId id) { + AMS_ASSERT(this->interface); + return this->interface->UnregisterProgramPath(id); + } + + void RedirectProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface); + if (hos::GetVersion() >= hos::Version_900) { + R_ABORT_UNLESS(this->interface->RedirectProgramPath(path, id, owner_id)); + } else { + R_ABORT_UNLESS(this->interface->RedirectProgramPathDeprecated(path, id)); + } + } + + Result ResolveHtmlDocumentPath(Path *out, ncm::ProgramId id) { + AMS_ASSERT(this->interface); + return this->interface->ResolveHtmlDocumentPath(out, id); + } + + Result RegisterHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface); + if (hos::GetVersion() >= hos::Version_900) { + return this->interface->RegisterHtmlDocumentPath(path, id, owner_id); + } else { + return this->interface->RegisterHtmlDocumentPathDeprecated(path, id); + } + } + + Result UnregisterHtmlDocumentPath(ncm::ProgramId id) { + AMS_ASSERT(this->interface); + return this->interface->UnregisterHtmlDocumentPath(id); + } + + void RedirectHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + AMS_ASSERT(this->interface); + if (hos::GetVersion() >= hos::Version_900) { + R_ABORT_UNLESS(this->interface->RedirectHtmlDocumentPath(path, id, owner_id)); + } else { + R_ABORT_UNLESS(this->interface->RedirectHtmlDocumentPathDeprecated(path, id)); + } + } + + Result Refresh() { + AMS_ASSERT(this->interface); + return this->interface->Refresh(); + } + + Result RefreshExcluding(const ncm::ProgramId *excluding_ids, size_t num_ids) { + AMS_ASSERT(this->interface); + return this->interface->RefreshExcluding(sf::InArray(excluding_ids, num_ids)); + } + + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp new file mode 100644 index 000000000..5b15c5135 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::lr { + + struct alignas(4) Path : ams::sf::LargeData { + char str[fs::EntryNameLengthMax]; + + static constexpr Path Encode(const char *p) { + Path path = {}; + /* Copy C string to path, terminating when a null byte is found. */ + for (size_t i = 0; i < sizeof(path) - 1; i++) { + path.str[i] = p[i]; + if (p[i] == '\x00') { + break; + } + } + return path; + } + + constexpr inline size_t GetLength() const { + /* Determine length from the first null byte occurence. */ + size_t len = 0; + for (size_t i = 0; i < sizeof(this->str) - 1 && this->str[i] != '\x00'; i++) { + len++; + } + return len; + } + + constexpr inline bool IsValid() const { + /* Determine validity by presence of a terminating null byte. */ + for (size_t i = 0; i < sizeof(this->str); i++) { + if (this->str[i] == '\x00') { + return true; + } + } + return false; + } + }; + + static_assert(std::is_pod::value && sizeof(Path) == fs::EntryNameLengthMax); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index d84d22ed3..cffff25a4 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -16,4 +16,14 @@ #pragma once -#include "ncm/ncm_types.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_api.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_api.hpp new file mode 100644 index 000000000..a07fb6739 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_api.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ncm { + + /* Management. */ + void Initialize(); + void Finalize(); + + void InitializeWithObject(std::shared_ptr manager_object); + + /* Service API. */ + Result CreateContentStorage(StorageId storage_id); + Result CreateContentMetaDatabase(StorageId storage_id); + + Result VerifyContentStorage(StorageId storage_id); + Result VerifyContentMetaDatabase(StorageId storage_id); + + Result OpenContentStorage(ContentStorage *out, StorageId storage_id); + Result OpenContentMetaDatabase(ContentMetaDatabase *out, StorageId storage_id); + + Result CleanupContentMetaDatabase(StorageId storage_id); + + Result ActivateContentStorage(StorageId storage_id); + Result InactivateContentStorage(StorageId storage_id); + + Result ActivateContentMetaDatabase(StorageId storage_id); + Result InactivateContentMetaDatabase(StorageId storage_id); + + Result InvalidateRightsIdCache(); + + /* Deprecated API. */ + Result CloseContentStorageForcibly(StorageId storage_id); + Result CloseContentMetaDatabaseForcibly(StorageId storage_id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp new file mode 100644 index 000000000..a01da6faa --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class AutoBuffer { + NON_COPYABLE(AutoBuffer); + private: + u8 *buffer; + size_t size; + public: + AutoBuffer() : buffer(nullptr), size(0) { /* ... */ } + + ~AutoBuffer() { + this->Reset(); + } + + AutoBuffer(AutoBuffer &&rhs) { + this->buffer = rhs.buffer; + this->size = rhs.size; + rhs.buffer = nullptr; + rhs.size = 0; + } + + AutoBuffer& operator=(AutoBuffer &&rhs) { + AutoBuffer(std::move(rhs)).Swap(*this); + return *this; + } + + void Swap(AutoBuffer &rhs) { + std::swap(this->buffer, rhs.buffer); + std::swap(this->size, rhs.size); + } + + void Reset() { + if (this->buffer != nullptr) { + std::free(this->buffer); + this->buffer = nullptr; + this->size = 0; + } + } + + u8 *Get() const { + return this->buffer; + } + + size_t GetSize() const { + return this->size; + } + + Result Initialize(size_t size) { + /* Check that we're not already initialized. */ + AMS_ABORT_UNLESS(this->buffer == nullptr); + + /* Allocate a buffer. */ + this->buffer = static_cast(std::malloc(size)); + R_UNLESS(this->buffer != nullptr, ResultAllocationFailed()); + + this->size = size; + return ResultSuccess(); + } + + Result Initialize(const void *buf, size_t size) { + /* Create a new buffer of the right size. */ + R_TRY(this->Initialize(size)); + + /* Copy the input data in. */ + std::memcpy(this->buffer, buf, size); + + return ResultSuccess(); + } + }; +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_bounded_map.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_bounded_map.hpp new file mode 100644 index 000000000..4d72c653b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_bounded_map.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + template + using BoundedMap = util::BoundedMap; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp new file mode 100644 index 000000000..2c727c843 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + struct alignas(4) ContentId { + util::Uuid uuid; + + bool operator==(const ContentId& other) const { + return this->uuid == other.uuid; + } + + bool operator!=(const ContentId& other) const { + return this->uuid != other.uuid; + } + + bool operator==(const util::Uuid& other) const { + return this->uuid == other; + } + + bool operator!=(const util::Uuid& other) const { + return this->uuid != other; + } + }; + + static_assert(alignof(ContentId) == 4); + + constexpr inline ContentId InvalidContentId = { util::InvalidUuid }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp new file mode 100644 index 000000000..95bff84ce --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id_utils.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ncm { + + constexpr inline size_t ContentIdStringLength = 2 * sizeof(ContentId); + constexpr inline size_t RightsIdStringLength = 2 * sizeof(fs::RightsId); + constexpr inline size_t TicketFileStringLength = RightsIdStringLength + 4; + constexpr inline size_t CertFileStringLength = RightsIdStringLength + 5; + + struct ContentIdString { + char data[ContentIdStringLength + 1]; + }; + + ContentIdString GetContentIdString(ContentId id); + + void GetStringFromContentId(char *dst, size_t dst_size, ContentId id); + + std::optional GetContentIdFromString(const char *str, size_t len); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp new file mode 100644 index 000000000..c0fcd21f8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + struct ContentInfo { + ContentId content_id; + u32 size_low; + u16 size_high; + ContentType content_type; + u8 id_offset; + + constexpr const ContentId &GetId() const { + return this->content_id; + } + + constexpr u64 GetSize() const { + return (static_cast(this->size_high) << 32) | static_cast(this->size_low); + } + + constexpr ContentType GetType() const { + return this->content_type; + } + + constexpr u8 GetIdOffset() const { + return this->id_offset; + } + + static constexpr ContentInfo Make(ContentId id, u64 size, ContentType type, u8 id_ofs = 0) { + const u32 size_low = size & 0xFFFFFFFFu; + const u16 size_high = static_cast(size >> 32); + return { + .content_id = id, + .size_low = size_low, + .size_high = size_high, + .content_type = type, + .id_offset = id_ofs, + }; + } + }; + + static_assert(sizeof(std::is_pod::value)); + static_assert(sizeof(ContentInfo) == 0x18); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp new file mode 100644 index 000000000..c29a7de62 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::ncm { + + struct Digest { + u8 data[crypto::Sha256Generator::HashSize]; + }; + + struct PackagedContentInfo { + Digest digest; + ContentInfo info; + + constexpr const ContentId &GetId() const { + return this->info.GetId(); + } + + constexpr const ContentType GetType() const { + return this->info.GetType(); + } + + constexpr const u8 GetIdOffset() const { + return this->info.GetIdOffset(); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_management_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_management_utils.hpp new file mode 100644 index 000000000..7218e58f7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_management_utils.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ncm { + + class ContentMetaDatabaseBuilder { + private: + ContentMetaDatabase *db; + private: + Result BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info); + public: + explicit ContentMetaDatabaseBuilder(ContentMetaDatabase *d) : db(d) { /* ... */ } + + Result BuildFromStorage(ContentStorage *storage); + Result BuildFromPackage(const char *package_root_path); + + Result Cleanup(); + }; + + Result ListApplicationPackage(s32 *out_count, ApplicationId *out_ids, s32 max_out_ids, const char *package_root_path); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp new file mode 100644 index 000000000..39b63231b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_config.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + struct ContentManagerConfig { + bool build_system_database; + bool import_database_from_system_on_sd; + + bool HasAnyConfig() const { + return this->ShouldBuildDatabase() || this->import_database_from_system_on_sd; + } + + bool ShouldBuildDatabase() const { + return hos::GetVersion() < hos::Version_400 || this->build_system_database; + } + + bool ShouldImportDatabaseFromSignedSystemPartitionOnSd() const { + return this->import_database_from_system_on_sd; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp new file mode 100644 index 000000000..fd274264a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ams::ncm { + + struct SystemSaveDataInfo { + u64 id; + u64 size; + u64 journal_size; + u32 flags; + fs::SaveDataSpaceId space_id; + }; + static_assert(std::is_pod::value); + + class ContentManagerImpl final : public IContentManager { + private: + constexpr static size_t MaxContentStorageRoots = 8; + constexpr static size_t MaxContentMetaDatabaseRoots = 8; + private: + struct ContentStorageRoot { + NON_COPYABLE(ContentStorageRoot); + NON_MOVEABLE(ContentStorageRoot); + + char mount_name[fs::MountNameLengthMax + 1]; + char path[128]; + StorageId storage_id; + fs::ContentStorageId content_storage_id; + std::shared_ptr content_storage; + + ContentStorageRoot() { /* ... */ } + }; + + struct ContentMetaDatabaseRoot { + NON_COPYABLE(ContentMetaDatabaseRoot); + NON_MOVEABLE(ContentMetaDatabaseRoot); + + char mount_name[fs::MountNameLengthMax + 1]; + char path[128]; + StorageId storage_id; + SystemSaveDataInfo info; + std::shared_ptr content_meta_database; + std::optional> kvs; + u32 max_content_metas; + + ContentMetaDatabaseRoot() { /* ... */ } + }; + private: + os::RecursiveMutex mutex; + bool initialized; + ContentStorageRoot content_storage_roots[MaxContentStorageRoots]; + ContentMetaDatabaseRoot content_meta_database_roots[MaxContentMetaDatabaseRoots]; + u32 num_content_storage_entries; + u32 num_content_meta_entries; + RightsIdCache rights_id_cache; + public: + ContentManagerImpl() : initialized(false) { /* ... */ }; + ~ContentManagerImpl(); + public: + Result Initialize(const ContentManagerConfig &config); + private: + /* Helpers. */ + Result GetContentStorageRoot(ContentStorageRoot **out, StorageId id); + Result GetContentMetaDatabaseRoot(ContentMetaDatabaseRoot **out, StorageId id); + + Result InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, fs::ContentStorageId content_storage_id); + Result InitializeGameCardContentStorageRoot(ContentStorageRoot *out); + + Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas); + Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas); + + Result BuildContentMetaDatabase(StorageId storage_id); + Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition); + Result ImportContentMetaDatabaseImpl(StorageId storage_id, const char *import_mount_name, const char *path); + + Result EnsureAndMountSystemSaveData(const char *mount, const SystemSaveDataInfo &info) const; + public: + /* Actual commands. */ + virtual Result CreateContentStorage(StorageId storage_id) override; + virtual Result CreateContentMetaDatabase(StorageId storage_id) override; + virtual Result VerifyContentStorage(StorageId storage_id) override; + virtual Result VerifyContentMetaDatabase(StorageId storage_id) override; + virtual Result OpenContentStorage(sf::Out> out, StorageId storage_id) override; + virtual Result OpenContentMetaDatabase(sf::Out> out, StorageId storage_id) override; + virtual Result CloseContentStorageForcibly(StorageId storage_id) override; + virtual Result CloseContentMetaDatabaseForcibly(StorageId storage_id) override; + virtual Result CleanupContentMetaDatabase(StorageId storage_id) override; + virtual Result ActivateContentStorage(StorageId storage_id) override; + virtual Result InactivateContentStorage(StorageId storage_id) override; + virtual Result ActivateContentMetaDatabase(StorageId storage_id) override; + virtual Result InactivateContentMetaDatabase(StorageId storage_id) override; + virtual Result InvalidateRightsIdCache() override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp new file mode 100644 index 000000000..cbb4ab5d6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::ncm { + + enum ContentMetaAttribute : u8 { + ContentMetaAttribute_None = (0 << 0), + ContentMetaAttribute_IncludesExFatDriver = (1 << 0), + ContentMetaAttribute_Rebootless = (1 << 1), + }; + + struct ContentMetaInfo { + u64 id; + u32 version; + ContentMetaType type; + u8 attributes; + u8 padding[2]; + + static constexpr ContentMetaInfo Make(u64 id, u32 version, ContentMetaType type, u8 attributes) { + return { + .id = id, + .version = version, + .type = type, + .attributes = attributes, + }; + } + + constexpr ContentMetaKey ToKey() { + return ContentMetaKey::Make(this->id, this->version, this->type); + } + }; + + static_assert(sizeof(ContentMetaInfo) == 0x10); + + struct ContentMetaHeader { + u16 extended_header_size; + u16 content_count; + u16 content_meta_count; + u8 attributes; + StorageId storage_id; + }; + + static_assert(sizeof(ContentMetaHeader) == 0x8); + + struct PackagedContentMetaHeader { + u64 id; + u32 version; + ContentMetaType type; + u8 reserved_0D; + u16 extended_header_size; + u16 content_count; + u16 content_meta_count; + u8 attributes; + u8 storage_id; + ContentInstallType install_type; + u8 reserved_17; + u32 required_download_system_version; + u8 reserved_1C[4]; + }; + static_assert(sizeof(PackagedContentMetaHeader) == 0x20); + static_assert(OFFSETOF(PackagedContentMetaHeader, reserved_0D) == 0x0D); + static_assert(OFFSETOF(PackagedContentMetaHeader, reserved_17) == 0x17); + static_assert(OFFSETOF(PackagedContentMetaHeader, reserved_1C) == 0x1C); + + struct ApplicationMetaExtendedHeader { + PatchId patch_id; + u32 required_system_version; + u32 required_application_version; + }; + + struct PatchMetaExtendedHeader { + ApplicationId application_id; + u32 required_system_version; + u32 extended_data_size; + u8 reserved[0x8]; + }; + + struct AddOnContentMetaExtendedHeader { + ApplicationId application_id; + u32 required_application_version; + u32 padding; + }; + + struct DeltaMetaExtendedHeader { + ApplicationId application_id; + u32 extended_data_size; + u32 padding; + }; + + template + class ContentMetaAccessor { + public: + using HeaderType = ContentMetaHeaderType; + using InfoType = ContentInfoType; + private: + void *data; + const size_t size; + bool is_header_valid; + private: + static size_t GetExtendedHeaderSize(ContentMetaType type) { + switch (type) { + case ContentMetaType::Application: return sizeof(ApplicationMetaExtendedHeader); + case ContentMetaType::Patch: return sizeof(PatchMetaExtendedHeader); + case ContentMetaType::AddOnContent: return sizeof(AddOnContentMetaExtendedHeader); + case ContentMetaType::Delta: return sizeof(DeltaMetaExtendedHeader); + default: return 0; + } + } + protected: + constexpr ContentMetaAccessor(const void *d, size_t sz) : data(const_cast(d)), size(sz), is_header_valid(true) { /* ... */ } + constexpr ContentMetaAccessor(void *d, size_t sz) : data(d), size(sz), is_header_valid(false) { /* ... */ } + + template + static constexpr size_t CalculateSizeImpl(size_t ext_header_size, size_t content_count, size_t content_meta_count, size_t extended_data_size, bool has_digest) { + return sizeof(NewHeaderType) + ext_header_size + content_count * sizeof(NewInfoType) + content_meta_count * sizeof(ContentMetaInfo) + extended_data_size + (has_digest ? sizeof(Digest) : 0); + } + + static constexpr size_t CalculateSize(ContentMetaType type, size_t content_count, size_t content_meta_count, size_t extended_data_size, bool has_digest = false) { + return CalculateSizeImpl(GetExtendedHeaderSize(type), content_count, content_meta_count, extended_data_size, has_digest); + } + + uintptr_t GetExtendedHeaderAddress() const { + return reinterpret_cast(this->data) + sizeof(HeaderType); + } + + uintptr_t GetContentInfoStartAddress() const { + return this->GetExtendedHeaderAddress() + this->GetExtendedHeaderSize(); + } + + uintptr_t GetContentInfoAddress(size_t i) const { + return this->GetContentInfoStartAddress() + i * sizeof(InfoType); + } + + uintptr_t GetContentMetaInfoStartAddress() const { + return this->GetContentInfoAddress(this->GetContentCount()); + } + + uintptr_t GetContentMetaInfoAddress(size_t i) const { + return this->GetContentMetaInfoStartAddress() + i * sizeof(ContentMetaInfo); + } + + uintptr_t GetExtendedDataAddress() const { + return this->GetContentMetaInfoAddress(this->GetContentMetaCount()); + } + + uintptr_t GetDigestAddress() const { + return this->GetExtendedDataAddress() + this->GetExtendedDataSize(); + } + + InfoType *GetWritableContentInfo(size_t i) const { + AMS_ABORT_UNLESS(i < this->GetContentCount()); + + return reinterpret_cast(this->GetContentInfoAddress(i)); + } + + InfoType *GetWritableContentInfo(ContentType type) const { + InfoType *found = nullptr; + for (size_t i = 0; i < this->GetContentCount(); i++) { + /* We want to find the info with the lowest id offset and the correct type. */ + InfoType *info = this->GetWritableContentInfo(i); + if (info->GetType() == type && (found == nullptr || info->GetIdOffset() < found->GetIdOffset())) { + found = info; + } + } + return found; + } + + InfoType *GetWritableContentInfo(ContentType type, u8 id_ofs) const { + for (size_t i = 0; i < this->GetContentCount(); i++) { + /* We want to find the info with the correct id offset and the correct type. */ + if (InfoType *info = this->GetWritableContentInfo(i); info->GetType() == type && info->GetIdOffset() == id_ofs) { + return info; + } + } + return nullptr; + } + + public: + const void *GetData() const { + return this->data; + } + + size_t GetSize() const { + return this->size; + } + + const HeaderType *GetHeader() const { + AMS_ABORT_UNLESS(this->is_header_valid); + return static_cast(this->data); + } + + ContentMetaKey GetKey() const { + auto header = this->GetHeader(); + return ContentMetaKey::Make(header->id, header->version, header->type, header->install_type); + } + + size_t GetExtendedHeaderSize() const { + return this->GetHeader()->extended_header_size; + } + + template + const ExtendedHeaderType *GetExtendedHeader() const { + return reinterpret_cast(this->GetExtendedHeaderAddress()); + } + + size_t GetContentCount() const { + return this->GetHeader()->content_count; + } + + const InfoType *GetContentInfo(size_t i) const { + AMS_ABORT_UNLESS(i < this->GetContentCount()); + + return this->GetWritableContentInfo(i); + } + + const InfoType *GetContentInfo(ContentType type) const { + return this->GetWritableContentInfo(type); + } + + const InfoType *GetContentInfo(ContentType type, u8 id_ofs) const { + return this->GetWritableContentInfo(type, id_ofs); + } + + size_t GetContentMetaCount() const { + return this->GetHeader()->content_meta_count; + } + + const ContentMetaInfo *GetContentMetaInfo(size_t i) const { + AMS_ABORT_UNLESS(i < this->GetContentMetaCount()); + + return reinterpret_cast(this->GetContentMetaInfoAddress(i)); + } + + size_t GetExtendedDataSize() const { + switch (this->GetHeader()->type) { + case ContentMetaType::Patch: return this->GetExtendedHeader()->extended_data_size; + case ContentMetaType::Delta: return this->GetExtendedHeader()->extended_data_size; + default: return 0; + } + } + + const void *GetExtendedData() const { + return reinterpret_cast(this->GetExtendedDataAddress()); + } + + const Digest *GetDigest() const { + return reinterpret_cast(this->GetDigestAddress()); + } + + bool HasContent(const ContentId &id) const { + for (size_t i = 0; i < this->GetContentCount(); i++) { + if (id == this->GetContentInfo(i)->GetId()) { + return true; + } + } + return false; + } + + std::optional GetApplicationId(const ContentMetaKey &key) const { + switch (key.type) { + case ContentMetaType::Application: return ApplicationId{ key.id }; + case ContentMetaType::Patch: return this->GetExtendedHeader()->application_id; + case ContentMetaType::AddOnContent: return this->GetExtendedHeader()->application_id; + case ContentMetaType::Delta: return this->GetExtendedHeader()->application_id; + default: return std::nullopt; + } + } + + std::optional GetApplicationId() const { + return this->GetApplicationId(this->GetKey()); + } + }; + + class ContentMetaReader : public ContentMetaAccessor { + public: + constexpr ContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } + + using ContentMetaAccessor::CalculateSize; + }; + + class PackagedContentMetaReader : public ContentMetaAccessor { + public: + constexpr PackagedContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } + + size_t CalculateConvertContentMetaSize() const; + + void ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta); + + size_t CountDeltaFragments() const; + + static constexpr size_t CalculateSize(ContentMetaType type, size_t content_count, size_t content_meta_count, size_t extended_data_size) { + return ContentMetaAccessor::CalculateSize(type, content_count, content_meta_count, extended_data_size, true); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp new file mode 100644 index 000000000..b41bcb532 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_database.hpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class ContentMetaDatabase { + NON_COPYABLE(ContentMetaDatabase); + public: + struct ListCount { + s32 written; + s32 total; + }; + private: + std::shared_ptr interface; + public: + ContentMetaDatabase() { /* ... */ } + explicit ContentMetaDatabase(std::shared_ptr intf) : interface(std::move(intf)) { /* ... */ } + + ContentMetaDatabase(ContentMetaDatabase &&rhs) { + this->interface = std::move(rhs.interface); + } + + ContentMetaDatabase &operator=(ContentMetaDatabase &&rhs) { + ContentMetaDatabase(std::move(rhs)).Swap(*this); + return *this; + } + + void Swap(ContentMetaDatabase &rhs) { + std::swap(this->interface, rhs.interface); + } + public: + Result Set(const ContentMetaKey &key, const void *buf, size_t size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Set(key, sf::InBuffer(buf, size)); + } + + Result Get(size_t *out_size, void *dst, size_t dst_size, const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + u64 size; + R_TRY(this->interface->Get(std::addressof(size), key, sf::OutBuffer(dst, dst_size))); + + *out_size = size; + return ResultSuccess(); + } + + #define AMS_NCM_DEFINE_GETTERS(Kind, IdType) \ + Result Get##Kind(ContentId *out, IdType##Id id, u32 version) { \ + return this->interface->GetContentIdByType(out, ContentMetaKey::MakeUnknownType(id.value, version), ContentType::Kind); \ + } \ + \ + Result GetLatest##Kind(ContentId *out, IdType##Id id) { \ + ContentMetaKey latest_key; \ + R_TRY(this->interface->GetLatestContentMetaKey(std::addressof(latest_key), id.value)); \ + return this->interface->GetContentIdByType(out, latest_key, ContentType::Kind); \ + } + + AMS_NCM_DEFINE_GETTERS(Program, Program) + AMS_NCM_DEFINE_GETTERS(Data, Data) + AMS_NCM_DEFINE_GETTERS(Control, Application) + AMS_NCM_DEFINE_GETTERS(HtmlDocument, Application) + AMS_NCM_DEFINE_GETTERS(LegalInformation, Application) + + #undef AMS_NCM_DEFINE_GETTERS + + Result Remove(const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Remove(key); + } + + Result Remove(SystemProgramId id, u32 version) { + return this->Remove(ContentMetaKey::Make(id, version)); + } + + Result Remove(SystemDataId id, u32 version) { + return this->Remove(ContentMetaKey::Make(id, version)); + } + + Result Remove(ApplicationId id, u32 version) { + return this->Remove(ContentMetaKey::Make(id, version)); + } + + Result GetContentIdByType(ContentId *out_content_id, const ContentMetaKey &key, ContentType type) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetContentIdByType(out_content_id, key, type); + } + + Result GetContentIdByTypeAndIdOffset(ContentId *out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetContentIdByTypeAndIdOffset(out_content_id, key, type, id_offset); + } + + ListCount ListApplication(ApplicationContentMetaKey *dst, size_t dst_size) { + ListCount lc = {}; + R_ABORT_UNLESS(this->interface->ListApplication(std::addressof(lc.total), std::addressof(lc.written), sf::OutArray(dst, dst_size), ContentMetaType::Unknown)); + return lc; + } + + ListCount ListContentMeta(ContentMetaKey *dst, size_t dst_size, ContentMetaType type = ContentMetaType::Unknown, ApplicationId app_id = InvalidApplicationId, u64 min = std::numeric_limits::min(), u64 max = std::numeric_limits::max(), ContentInstallType install_type = ContentInstallType::Full) { + ListCount lc = {}; + R_ABORT_UNLESS(this->interface->List(std::addressof(lc.total), std::addressof(lc.written), sf::OutArray(dst, dst_size), type, app_id, min, max, install_type)); + return lc; + } + + Result GetLatest(ContentMetaKey *out_key, u64 id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetLatestContentMetaKey(out_key, id); + } + + Result ListContentInfo(s32 *out_count, ContentInfo *dst, size_t dst_size, const ContentMetaKey &key, s32 offset) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ListContentInfo(out_count, sf::OutArray(dst, dst_size), key, offset); + } + + Result ListContentMetaInfo(s32 *out_count, ContentMetaInfo *dst, size_t dst_size, const ContentMetaKey &key, s32 offset) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ListContentMetaInfo(out_count, sf::OutArray(dst, dst_size), key, offset); + } + + Result Has(bool *out, const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Has(out, key); + } + + Result HasAll(bool *out, const ContentMetaKey *keys, size_t num_keys) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->HasAll(out, sf::InArray(keys, num_keys)); + } + + Result HasContent(bool *out, const ContentMetaKey &key, const ContentId &content_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->HasContent(out, key, content_id); + } + + Result GetSize(size_t *out_size, const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + u64 size; + R_TRY(this->interface->GetSize(std::addressof(size), key)); + + *out_size = size; + return ResultSuccess(); + } + + Result GetRequiredSystemVersion(u32 *out_version, const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetRequiredSystemVersion(out_version, key); + } + + Result GetPatchId(PatchId *out_patch_id, const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetPatchId(out_patch_id, key); + } + + Result DisableForcibly() { + AMS_ASSERT(this->interface != nullptr); + return this->interface->DisableForcibly(); + } + + Result LookupOrphanContent(bool *out_orphaned, ContentId *content_list, size_t count) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->LookupOrphanContent(sf::OutArray(out_orphaned, count), sf::InArray(content_list, count)); + } + + Result Commit() { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Commit(); + } + + Result GetAttributes(u8 *out_attributes, const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetAttributes(out_attributes, key); + } + + Result GetRequiredApplicationVersion(u32 *out_version, const ContentMetaKey &key) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetRequiredApplicationVersion(out_version, key); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_id.hpp new file mode 100644 index 000000000..e4733be56 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_id.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + struct ApplicationId { + u64 value; + + constexpr operator ProgramId() const { + return { this->value }; + } + + static const ApplicationId Start; + static const ApplicationId End; + }; + + constexpr inline const ApplicationId InvalidApplicationId = {}; + + inline constexpr const ApplicationId ApplicationId::Start = { 0x0100000000010000ul }; + inline constexpr const ApplicationId ApplicationId::End = { 0x01FFFFFFFFFFFFFFul }; + + inline constexpr bool IsApplicationId(const ProgramId &program_id) { + return ApplicationId::Start <= program_id && program_id <= ApplicationId::End; + } + + inline constexpr bool IsApplicationId(const ApplicationId &id) { + return true; + } + + struct ApplicationGroupId { + u64 value; + }; + + struct PatchId { + u64 value; + + constexpr operator ProgramId() const { + return { this->value }; + } + }; + + struct PatchGroupId { + u64 value; + }; + + struct AddOnContentId { + u64 value; + + constexpr operator DataId() const { + return { this->value }; + } + }; + + struct DeltaId { + u64 value; + + constexpr operator ProgramId() const { + return { this->value }; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp new file mode 100644 index 000000000..2b796d5b0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::ncm { + + enum class ContentInstallType : u8 { + Full = 0, + FragmentOnly = 1, + Unknown = 7, + }; + + struct ContentMetaKey { + u64 id; + u32 version; + ContentMetaType type; + ContentInstallType install_type; + u8 padding[2]; + + bool operator<(const ContentMetaKey& rhs) const { + return std::tie(this->id, this->version, this->type, this->install_type) < std::tie(rhs.id, rhs.version, rhs.type, rhs.install_type); + } + + constexpr bool operator==(const ContentMetaKey& rhs) const { + return std::tie(this->id, this->version, this->type, this->install_type) == std::tie(rhs.id, rhs.version, rhs.type, rhs.install_type); + } + + constexpr bool operator!=(const ContentMetaKey& rhs) const { + return !(*this == rhs); + } + + static constexpr ContentMetaKey MakeUnknownType(u64 id, u32 version) { + return { .id = id, .version = version, .type = ContentMetaType::Unknown }; + } + + static constexpr ContentMetaKey Make(u64 id, u32 version, ContentMetaType type) { + return { .id = id, .version = version, .type = type }; + } + + static constexpr ContentMetaKey Make(u64 id, u32 version, ContentMetaType type, ContentInstallType install_type) { + return { .id = id, .version = version, .type = type, .install_type = install_type }; + } + + static constexpr ContentMetaKey Make(SystemProgramId id, u32 version) { + return { .id = id.value, .version = version, .type = ContentMetaType::SystemProgram }; + } + + static constexpr ContentMetaKey Make(SystemDataId id, u32 version) { + return { .id = id.value, .version = version, .type = ContentMetaType::SystemData }; + } + + static constexpr ContentMetaKey Make(SystemUpdateId id, u32 version) { + return { .id = id.value, .version = version, .type = ContentMetaType::SystemUpdate }; + } + + static constexpr ContentMetaKey Make(ApplicationId id, u32 version) { + return { .id = id.value, .version = version, .type = ContentMetaType::Application }; + } + + static constexpr ContentMetaKey Make(PatchId id, u32 version) { + return { .id = id.value, .version = version, .type = ContentMetaType::Patch }; + } + + static constexpr ContentMetaKey Make(PatchId id, u32 version, ContentInstallType install_type) { + return { .id = id.value, .version = version, .type = ContentMetaType::Patch, .install_type = install_type }; + } + + static constexpr ContentMetaKey Make(DeltaId id, u32 version) { + return { .id = id.value, .version = version, .type = ContentMetaType::Delta }; + } + }; + + static_assert(sizeof(ContentMetaKey) == 0x10); + + struct ApplicationContentMetaKey { + ContentMetaKey key; + ncm::ApplicationId application_id; + }; + + static_assert(sizeof(ApplicationContentMetaKey) == 0x18); + + struct StorageContentMetaKey { + ContentMetaKey key; + StorageId storage_id; + u8 reserved[7]; + + constexpr bool operator==(StorageContentMetaKey &rhs) const { + return this->key == rhs.key && this->storage_id == rhs.storage_id; + } + + constexpr bool operator<(StorageContentMetaKey &rhs) const { + return this->key == rhs.key ? this->storage_id < rhs.storage_id : this->key < rhs.key; + } + }; + static_assert(sizeof(StorageContentMetaKey) == 0x18); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_type.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_type.hpp new file mode 100644 index 000000000..20252c844 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_type.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + enum class ContentMetaType : u8 { + Unknown = 0x0, + SystemProgram = 0x1, + SystemData = 0x2, + SystemUpdate = 0x3, + BootImagePackage = 0x4, + BootImagePackageSafe = 0x5, + Application = 0x80, + Patch = 0x81, + AddOnContent = 0x82, + Delta = 0x83, + }; + + const char *GetContentMetaTypeString(ContentMetaType type); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp new file mode 100644 index 000000000..6673e063d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::ncm { + + Result ReadContentMetaPath(AutoBuffer *out, const char *path); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_storage.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_storage.hpp new file mode 100644 index 000000000..b776399d8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_storage.hpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class ContentStorage { + NON_COPYABLE(ContentStorage); + private: + std::shared_ptr interface; + public: + ContentStorage() { /* ... */ } + explicit ContentStorage(std::shared_ptr intf) : interface(std::move(intf)) { /* ... */ } + + ContentStorage(ContentStorage &&rhs) { + this->interface = std::move(rhs.interface); + } + + ContentStorage &operator=(ContentStorage &&rhs) { + ContentStorage(std::move(rhs)).Swap(*this); + return *this; + } + + void Swap(ContentStorage &rhs) { + std::swap(this->interface, rhs.interface); + } + public: + PlaceHolderId GeneratePlaceHolderId() { + AMS_ASSERT(this->interface != nullptr); + + PlaceHolderId id; + R_ABORT_UNLESS(this->interface->GeneratePlaceHolderId(std::addressof(id))); + return id; + } + + Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->CreatePlaceHolder(placeholder_id, content_id, size); + } + + Result DeletePlaceHolder(PlaceHolderId placeholder_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->DeletePlaceHolder(placeholder_id); + } + + Result HasPlaceHolder(bool *out, PlaceHolderId placeholder_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->HasPlaceHolder(out, placeholder_id); + } + + Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const void *buf, size_t size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->WritePlaceHolder(placeholder_id, offset, sf::InBuffer(buf, size)); + } + + Result Register(PlaceHolderId placeholder_id, ContentId content_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Register(placeholder_id, content_id); + } + + Result Delete(ContentId content_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Delete(content_id); + } + + Result Has(bool *out, ContentId content_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->Has(out, content_id); + } + + void GetPath(Path *out, ContentId content_id) { + AMS_ASSERT(this->interface != nullptr); + R_ABORT_UNLESS(this->interface->GetPath(out, content_id)); + } + + void GetPlaceHolderPath(Path *out, PlaceHolderId placeholder_id) { + AMS_ASSERT(this->interface != nullptr); + R_ABORT_UNLESS(this->interface->GetPlaceHolderPath(out, placeholder_id)); + } + + Result CleanupAllPlaceHolder() { + AMS_ASSERT(this->interface != nullptr); + return this->interface->CleanupAllPlaceHolder(); + } + + Result ListPlaceHolder(s32 *out_count, PlaceHolderId *out_list, size_t out_list_size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ListPlaceHolder(out_count, sf::OutArray(out_list, out_list_size)); + } + + Result GetContentCount(s32 *out_count) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetContentCount(out_count); + } + + Result ListContentId(s32 *out_count, ContentId *out_list, size_t out_list_size, s32 offset) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ListContentId(out_count, sf::OutArray(out_list, out_list_size), offset); + } + + Result GetSize(s64 *out_size, ContentId content_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetSizeFromContentId(out_size, content_id); + } + + Result GetSize(s64 *out_size, PlaceHolderId placeholder_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetSizeFromPlaceHolderId(out_size, placeholder_id); + } + + Result DisableForcibly() { + AMS_ASSERT(this->interface != nullptr); + return this->interface->DisableForcibly(); + } + + Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->RevertToPlaceHolder(placeholder_id, old_content_id, new_content_id); + } + + Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->SetPlaceHolderSize(placeholder_id, size); + } + + Result ReadContentIdFile(void *dst, size_t size, ContentId content_id, s64 offset) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->ReadContentIdFile(sf::OutBuffer(dst, size), content_id, offset); + } + + Result GetRightsId(ams::fs::RightsId *out_rights_id, PlaceHolderId placeholder_id) { + AMS_ASSERT(this->interface != nullptr); + AMS_ABORT_UNLESS(hos::GetVersion() < hos::Version_300); + return this->interface->GetRightsIdFromPlaceHolderIdDeprecated(out_rights_id, placeholder_id); + } + + Result GetRightsId(ncm::RightsId *out_rights_id, PlaceHolderId placeholder_id) { + AMS_ASSERT(this->interface != nullptr); + AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_300); + return this->interface->GetRightsIdFromPlaceHolderId(out_rights_id, placeholder_id); + } + + Result GetRightsId(ams::fs::RightsId *out_rights_id, ContentId content_id) { + AMS_ASSERT(this->interface != nullptr); + AMS_ABORT_UNLESS(hos::GetVersion() < hos::Version_300); + return this->interface->GetRightsIdFromContentIdDeprecated(out_rights_id, content_id); + } + + Result GetRightsId(ncm::RightsId *out_rights_id, ContentId content_id) { + AMS_ASSERT(this->interface != nullptr); + AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_300); + return this->interface->GetRightsIdFromContentId(out_rights_id, content_id); + } + + Result WriteContentForDebug(ContentId content_id, s64 offset, const void *buf, size_t size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->WriteContentForDebug(content_id, offset, sf::InBuffer(buf, size)); + } + + Result GetFreeSpaceSize(s64 *out_size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetFreeSpaceSize(out_size); + } + + Result GetTotalSpaceSize(s64 *out_size) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetTotalSpaceSize(out_size); + } + + Result FlushPlaceHolder() { + AMS_ASSERT(this->interface != nullptr); + return this->interface->FlushPlaceHolder(); + } + + Result RepairInvalidFileAttribute() { + AMS_ASSERT(this->interface != nullptr); + return this->interface->RepairInvalidFileAttribute(); + } + + Result GetRightsIdFromPlaceHolderIdWithCache(ncm::RightsId *out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) { + AMS_ASSERT(this->interface != nullptr); + return this->interface->GetRightsIdFromPlaceHolderIdWithCache(out_rights_id, placeholder_id, cache_content_id); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_type.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_type.hpp new file mode 100644 index 000000000..1aa71c6a7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_type.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + enum class ContentType : u8 { + Meta = 0, + Program = 1, + Data = 2, + Control = 3, + HtmlDocument = 4, + LegalInformation = 5, + DeltaFragment = 6, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_data_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_data_id.hpp new file mode 100644 index 000000000..02d926be3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_data_id.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + struct DataId { + u64 value; + + static const DataId Invalid; + }; + + inline constexpr bool operator==(const DataId &lhs, const DataId &rhs) { + return lhs.value == rhs.value; + } + + inline constexpr bool operator!=(const DataId &lhs, const DataId &rhs) { + return lhs.value != rhs.value; + } + + inline constexpr bool operator<(const DataId &lhs, const DataId &rhs) { + return lhs.value < rhs.value; + } + + inline constexpr bool operator<=(const DataId &lhs, const DataId &rhs) { + return lhs.value <= rhs.value; + } + + inline constexpr bool operator>(const DataId &lhs, const DataId &rhs) { + return lhs.value > rhs.value; + } + + inline constexpr bool operator>=(const DataId &lhs, const DataId &rhs) { + return lhs.value >= rhs.value; + } + + inline constexpr const DataId DataId::Invalid = {}; + inline constexpr const DataId InvalidDataId = DataId::Invalid; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp new file mode 100644 index 000000000..87617cea1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + class IContentManager : public sf::IServiceObject { + protected: + enum class CommandId { + CreateContentStorage = 0, + CreateContentMetaDatabase = 1, + VerifyContentStorage = 2, + VerifyContentMetaDatabase = 3, + OpenContentStorage = 4, + OpenContentMetaDatabase = 5, + CloseContentStorageForcibly = 6, + CloseContentMetaDatabaseForcibly = 7, + CleanupContentMetaDatabase = 8, + ActivateContentStorage = 9, + InactivateContentStorage = 10, + ActivateContentMetaDatabase = 11, + InactivateContentMetaDatabase = 12, + InvalidateRightsIdCache = 13, + }; + public: + virtual Result CreateContentStorage(StorageId storage_id) = 0; + virtual Result CreateContentMetaDatabase(StorageId storage_id) = 0; + virtual Result VerifyContentStorage(StorageId storage_id) = 0; + virtual Result VerifyContentMetaDatabase(StorageId storage_id) = 0; + virtual Result OpenContentStorage(sf::Out> out, StorageId storage_id) = 0; + virtual Result OpenContentMetaDatabase(sf::Out> out, StorageId storage_id) = 0; + virtual Result CloseContentStorageForcibly(StorageId storage_id) = 0; + virtual Result CloseContentMetaDatabaseForcibly(StorageId storage_id) = 0; + virtual Result CleanupContentMetaDatabase(StorageId storage_id) = 0; + virtual Result ActivateContentStorage(StorageId storage_id) = 0; + virtual Result InactivateContentStorage(StorageId storage_id) = 0; + virtual Result ActivateContentMetaDatabase(StorageId storage_id) = 0; + virtual Result InactivateContentMetaDatabase(StorageId storage_id) = 0; + virtual Result InvalidateRightsIdCache() = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(CreateContentStorage), + MAKE_SERVICE_COMMAND_META(CreateContentMetaDatabase), + MAKE_SERVICE_COMMAND_META(VerifyContentStorage), + MAKE_SERVICE_COMMAND_META(VerifyContentMetaDatabase), + MAKE_SERVICE_COMMAND_META(OpenContentStorage), + MAKE_SERVICE_COMMAND_META(OpenContentMetaDatabase), + MAKE_SERVICE_COMMAND_META(CloseContentStorageForcibly, hos::Version_100, hos::Version_100), + MAKE_SERVICE_COMMAND_META(CloseContentMetaDatabaseForcibly, hos::Version_100, hos::Version_100), + MAKE_SERVICE_COMMAND_META(CleanupContentMetaDatabase), + MAKE_SERVICE_COMMAND_META(ActivateContentStorage, hos::Version_200), + MAKE_SERVICE_COMMAND_META(InactivateContentStorage, hos::Version_200), + MAKE_SERVICE_COMMAND_META(ActivateContentMetaDatabase, hos::Version_200), + MAKE_SERVICE_COMMAND_META(InactivateContentMetaDatabase, hos::Version_200), + MAKE_SERVICE_COMMAND_META(InvalidateRightsIdCache, hos::Version_900), + }; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp new file mode 100644 index 000000000..cc3e06b12 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + class IContentMetaDatabase : public sf::IServiceObject { + protected: + enum class CommandId { + Set = 0, + Get = 1, + Remove = 2, + GetContentIdByType = 3, + ListContentInfo = 4, + List = 5, + GetLatestContentMetaKey = 6, + ListApplication = 7, + Has = 8, + HasAll = 9, + GetSize = 10, + GetRequiredSystemVersion = 11, + GetPatchId = 12, + DisableForcibly = 13, + LookupOrphanContent = 14, + Commit = 15, + HasContent = 16, + ListContentMetaInfo = 17, + GetAttributes = 18, + GetRequiredApplicationVersion = 19, + GetContentIdByTypeAndIdOffset = 20, + }; + public: + /* Actual commands. */ + virtual Result Set(const ContentMetaKey &key, sf::InBuffer value) = 0; + virtual Result Get(sf::Out out_size, const ContentMetaKey &key, sf::OutBuffer out_value) = 0; + virtual Result Remove(const ContentMetaKey &key) = 0; + virtual Result GetContentIdByType(sf::Out out_content_id, const ContentMetaKey &key, ContentType type) = 0; + virtual Result ListContentInfo(sf::Out out_entries_written, const sf::OutArray &out_info, const ContentMetaKey &key, s32 offset) = 0; + virtual Result List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) = 0; + virtual Result GetLatestContentMetaKey(sf::Out out_key, u64 id) = 0; + virtual Result ListApplication(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_keys, ContentMetaType meta_type) = 0; + virtual Result Has(sf::Out out, const ContentMetaKey &key) = 0; + virtual Result HasAll(sf::Out out, const sf::InArray &keys) = 0; + virtual Result GetSize(sf::Out out_size, const ContentMetaKey &key) = 0; + virtual Result GetRequiredSystemVersion(sf::Out out_version, const ContentMetaKey &key) = 0; + virtual Result GetPatchId(sf::Out out_patch_id, const ContentMetaKey &key) = 0; + virtual Result DisableForcibly() = 0; + virtual Result LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) = 0; + virtual Result Commit() = 0; + virtual Result HasContent(sf::Out out, const ContentMetaKey &key, const ContentId &content_id) = 0; + virtual Result ListContentMetaInfo(sf::Out out_entries_written, const sf::OutArray &out_meta_info, const ContentMetaKey &key, s32 offset) = 0; + virtual Result GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) = 0; + virtual Result GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) = 0; + virtual Result GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(Set), + MAKE_SERVICE_COMMAND_META(Get), + MAKE_SERVICE_COMMAND_META(Remove), + MAKE_SERVICE_COMMAND_META(GetContentIdByType), + MAKE_SERVICE_COMMAND_META(ListContentInfo), + MAKE_SERVICE_COMMAND_META(List), + MAKE_SERVICE_COMMAND_META(GetLatestContentMetaKey), + MAKE_SERVICE_COMMAND_META(ListApplication), + MAKE_SERVICE_COMMAND_META(Has), + MAKE_SERVICE_COMMAND_META(HasAll), + MAKE_SERVICE_COMMAND_META(GetSize), + MAKE_SERVICE_COMMAND_META(GetRequiredSystemVersion), + MAKE_SERVICE_COMMAND_META(GetPatchId), + MAKE_SERVICE_COMMAND_META(DisableForcibly), + MAKE_SERVICE_COMMAND_META(LookupOrphanContent), + MAKE_SERVICE_COMMAND_META(Commit), + MAKE_SERVICE_COMMAND_META(HasContent), + MAKE_SERVICE_COMMAND_META(ListContentMetaInfo), + MAKE_SERVICE_COMMAND_META(GetAttributes), + MAKE_SERVICE_COMMAND_META(GetRequiredApplicationVersion, hos::Version_200), + MAKE_SERVICE_COMMAND_META(GetContentIdByTypeAndIdOffset, hos::Version_500), + }; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp new file mode 100644 index 000000000..6e40714bd --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::ncm { + + class IContentStorage : public sf::IServiceObject { + NON_COPYABLE(IContentStorage); + NON_MOVEABLE(IContentStorage); + protected: + enum class CommandId { + GeneratePlaceHolderId = 0, + CreatePlaceHolder = 1, + DeletePlaceHolder = 2, + HasPlaceHolder = 3, + WritePlaceHolder = 4, + Register = 5, + Delete = 6, + Has = 7, + GetPath = 8, + GetPlaceHolderPath = 9, + CleanupAllPlaceHolder = 10, + ListPlaceHolder = 11, + GetContentCount = 12, + ListContentId = 13, + GetSizeFromContentId = 14, + DisableForcibly = 15, + RevertToPlaceHolder = 16, + SetPlaceHolderSize = 17, + ReadContentIdFile = 18, + GetRightsIdFromPlaceHolderIdDeprecated = 19, + GetRightsIdFromPlaceHolderId = 19, + GetRightsIdFromContentIdDeprecated = 20, + GetRightsIdFromContentId = 20, + WriteContentForDebug = 21, + GetFreeSpaceSize = 22, + GetTotalSpaceSize = 23, + FlushPlaceHolder = 24, + GetSizeFromPlaceHolderId = 25, + RepairInvalidFileAttribute = 26, + GetRightsIdFromPlaceHolderIdWithCache = 27, + }; + public: + IContentStorage() { /* ... */ } + public: + virtual Result GeneratePlaceHolderId(sf::Out out) = 0; + virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) = 0; + virtual Result DeletePlaceHolder(PlaceHolderId placeholder_id) = 0; + virtual Result HasPlaceHolder(sf::Out out, PlaceHolderId placeholder_id) = 0; + virtual Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, sf::InBuffer data) = 0; + virtual Result Register(PlaceHolderId placeholder_id, ContentId content_id) = 0; + virtual Result Delete(ContentId content_id) = 0; + virtual Result Has(sf::Out out, ContentId content_id) = 0; + virtual Result GetPath(sf::Out out, ContentId content_id) = 0; + virtual Result GetPlaceHolderPath(sf::Out out, PlaceHolderId placeholder_id) = 0; + virtual Result CleanupAllPlaceHolder() = 0; + virtual Result ListPlaceHolder(sf::Out out_count, const sf::OutArray &out_buf) = 0; + virtual Result GetContentCount(sf::Out out_count) = 0; + virtual Result ListContentId(sf::Out out_count, const sf::OutArray &out_buf, s32 start_offset) = 0; + virtual Result GetSizeFromContentId(sf::Out out_size, ContentId content_id) = 0; + virtual Result DisableForcibly() = 0; + virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) = 0; + virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) = 0; + virtual Result ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, s64 offset) = 0; + virtual Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out out_rights_id, PlaceHolderId placeholder_id) = 0; + virtual Result GetRightsIdFromPlaceHolderId(sf::Out out_rights_id, PlaceHolderId placeholder_id) = 0; + virtual Result GetRightsIdFromContentIdDeprecated(sf::Out out_rights_id, ContentId content_id) = 0; + virtual Result GetRightsIdFromContentId(sf::Out out_rights_id, ContentId content_id) = 0; + virtual Result WriteContentForDebug(ContentId content_id, s64 offset, sf::InBuffer data) = 0; + virtual Result GetFreeSpaceSize(sf::Out out_size) = 0; + virtual Result GetTotalSpaceSize(sf::Out out_size) = 0; + virtual Result FlushPlaceHolder() = 0; + virtual Result GetSizeFromPlaceHolderId(sf::Out out, PlaceHolderId placeholder_id) = 0; + virtual Result RepairInvalidFileAttribute() = 0; + virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(GeneratePlaceHolderId), + MAKE_SERVICE_COMMAND_META(CreatePlaceHolder), + MAKE_SERVICE_COMMAND_META(DeletePlaceHolder), + MAKE_SERVICE_COMMAND_META(HasPlaceHolder), + MAKE_SERVICE_COMMAND_META(WritePlaceHolder), + MAKE_SERVICE_COMMAND_META(Register), + MAKE_SERVICE_COMMAND_META(Delete), + MAKE_SERVICE_COMMAND_META(Has), + MAKE_SERVICE_COMMAND_META(GetPath), + MAKE_SERVICE_COMMAND_META(GetPlaceHolderPath), + MAKE_SERVICE_COMMAND_META(CleanupAllPlaceHolder), + MAKE_SERVICE_COMMAND_META(ListPlaceHolder), + MAKE_SERVICE_COMMAND_META(GeneratePlaceHolderId), + MAKE_SERVICE_COMMAND_META(GetContentCount), + MAKE_SERVICE_COMMAND_META(ListContentId), + MAKE_SERVICE_COMMAND_META(GetSizeFromContentId), + MAKE_SERVICE_COMMAND_META(DisableForcibly), + MAKE_SERVICE_COMMAND_META(RevertToPlaceHolder, hos::Version_200), + MAKE_SERVICE_COMMAND_META(SetPlaceHolderSize, hos::Version_200), + MAKE_SERVICE_COMMAND_META(ReadContentIdFile, hos::Version_200), + MAKE_SERVICE_COMMAND_META(GetRightsIdFromPlaceHolderIdDeprecated, hos::Version_200, hos::Version_200), + MAKE_SERVICE_COMMAND_META(GetRightsIdFromPlaceHolderId, hos::Version_300), + MAKE_SERVICE_COMMAND_META(GetRightsIdFromContentIdDeprecated, hos::Version_200, hos::Version_200), + MAKE_SERVICE_COMMAND_META(GetRightsIdFromContentId, hos::Version_300), + MAKE_SERVICE_COMMAND_META(WriteContentForDebug, hos::Version_200), + MAKE_SERVICE_COMMAND_META(GetFreeSpaceSize, hos::Version_200), + MAKE_SERVICE_COMMAND_META(GetTotalSpaceSize, hos::Version_200), + MAKE_SERVICE_COMMAND_META(FlushPlaceHolder, hos::Version_300), + MAKE_SERVICE_COMMAND_META(GetSizeFromPlaceHolderId, hos::Version_400), + MAKE_SERVICE_COMMAND_META(RepairInvalidFileAttribute, hos::Version_400), + MAKE_SERVICE_COMMAND_META(GetRightsIdFromPlaceHolderIdWithCache, hos::Version_800), + }; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_ids.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_ids.hpp new file mode 100644 index 000000000..fe76f948c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_ids.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_make_path.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_make_path.hpp new file mode 100644 index 000000000..1babc6db9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_make_path.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ncm { + + using MakeContentPathFunction = void (*)(PathString *out, ContentId content_id, const char *root_path); + using MakePlaceHolderPathFunction = void (*)(PathString *out, PlaceHolderId placeholder_id,const char *root_path); + + void MakeFlatContentFilePath(PathString *out, ContentId content_id, const char *root_path); + void MakeSha256HierarchicalContentFilePath_ForFat4KCluster(PathString *out, ContentId content_id, const char *root_path); + void MakeSha256HierarchicalContentFilePath_ForFat16KCluster(PathString *out, ContentId content_id, const char *root_path); + void MakeSha256HierarchicalContentFilePath_ForFat32KCluster(PathString *out, ContentId content_id, const char *root_path); + + size_t GetHierarchicalContentDirectoryDepth(MakeContentPathFunction func); + + void MakeFlatPlaceHolderFilePath(PathString *out, PlaceHolderId placeholder_id, const char *root_path); + void MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster(PathString *out, PlaceHolderId placeholder_id, const char *root_pathroot); + + size_t GetHierarchicalPlaceHolderDirectoryDepth(MakePlaceHolderPathFunction func); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp new file mode 100644 index 000000000..e3927c9a3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ncm { + + struct alignas(4) Path : ams::sf::LargeData { + char str[fs::EntryNameLengthMax]; + + static constexpr Path Encode(const char *p) { + Path path = {}; + /* Copy C string to path, terminating when a null byte is found. */ + for (size_t i = 0; i < sizeof(path) - 1; i++) { + path.str[i] = p[i]; + if (p[i] == '\x00') { + break; + } + } + return path; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_path_string.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_path_string.hpp new file mode 100644 index 000000000..5ec375421 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_path_string.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + using PathString = kvdb::BoundedString; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp new file mode 100644 index 000000000..9b41a66d1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + struct alignas(8) PlaceHolderId { + util::Uuid uuid; + + bool operator==(const PlaceHolderId& other) const { + return this->uuid == other.uuid; + } + + bool operator!=(const PlaceHolderId& other) const { + return this->uuid != other.uuid; + } + + bool operator==(const util::Uuid& other) const { + return this->uuid == other; + } + + bool operator!=(const util::Uuid& other) const { + return this->uuid != other; + } + }; + + static_assert(alignof(PlaceHolderId) == 8); + + constexpr inline PlaceHolderId InvalidPlaceHolderId = { util::InvalidUuid }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp new file mode 100644 index 000000000..20923ef52 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_id.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + struct ProgramId { + u64 value; + + static const ProgramId Invalid; + + inline explicit operator svc::ProgramId() const { + static_assert(sizeof(value) == sizeof(svc::ProgramId)); + return { this->value }; + } + }; + + inline constexpr bool operator==(const ProgramId &lhs, const ProgramId &rhs) { + return lhs.value == rhs.value; + } + + inline constexpr bool operator!=(const ProgramId &lhs, const ProgramId &rhs) { + return lhs.value != rhs.value; + } + + inline constexpr bool operator<(const ProgramId &lhs, const ProgramId &rhs) { + return lhs.value < rhs.value; + } + + inline constexpr bool operator<=(const ProgramId &lhs, const ProgramId &rhs) { + return lhs.value <= rhs.value; + } + + inline constexpr bool operator>(const ProgramId &lhs, const ProgramId &rhs) { + return lhs.value > rhs.value; + } + + inline constexpr bool operator>=(const ProgramId &lhs, const ProgramId &rhs) { + return lhs.value >= rhs.value; + } + + inline constexpr const ProgramId ProgramId::Invalid = {}; + inline constexpr const ProgramId InvalidProgramId = ProgramId::Invalid; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp new file mode 100644 index 000000000..3e2e53567 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + struct ProgramLocation { + ProgramId program_id; + u8 storage_id; + + static constexpr ProgramLocation Make(ProgramId program_id, StorageId storage_id) { + return { .program_id = program_id, .storage_id = static_cast(storage_id), }; + } + }; + static_assert(sizeof(ProgramLocation) == 0x10 && std::is_pod::value); + static_assert(sizeof(ProgramLocation) == sizeof(::NcmProgramLocation) && alignof(ProgramLocation) == alignof(::NcmProgramLocation), "ProgramLocation Libnx Compatibility"); + + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp new file mode 100644 index 000000000..03b520d94 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + struct RightsId { + fs::RightsId id; + u8 key_generation; + u8 reserved[7]; + }; + static_assert(sizeof(RightsId) == 0x18); + static_assert(std::is_pod::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp new file mode 100644 index 000000000..f29d01a8a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id_cache.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ncm { + + class RightsIdCache { + NON_COPYABLE(RightsIdCache); + NON_MOVEABLE(RightsIdCache); + private: + static constexpr size_t MaxEntries = 0x80; + private: + struct Entry { + public: + util::Uuid uuid; + ncm::RightsId rights_id; + u64 last_accessed; + }; + private: + Entry entries[MaxEntries]; + u64 counter; + os::Mutex mutex; + public: + RightsIdCache() { + this->Invalidate(); + } + + void Invalidate() { + this->counter = 2; + for (size_t i = 0; i < MaxEntries; i++) { + this->entries[i].last_accessed = 1; + } + } + + void Store(ContentId content_id, ncm::RightsId rights_id) { + std::scoped_lock lk(this->mutex); + Entry *eviction_candidate = &this->entries[0]; + + /* Find a suitable existing entry to store our new one at. */ + for (size_t i = 1; i < MaxEntries; i++) { + Entry *entry = &this->entries[i]; + + /* Change eviction candidates if the uuid already matches ours, or if the uuid doesn't already match and the last_accessed count is lower */ + if (content_id == entry->uuid || (content_id != eviction_candidate->uuid && entry->last_accessed < eviction_candidate->last_accessed)) { + eviction_candidate = entry; + } + } + + /* Update the cache. */ + eviction_candidate->uuid = content_id.uuid; + eviction_candidate->rights_id = rights_id; + eviction_candidate->last_accessed = this->counter++; + } + + bool Find(ncm::RightsId *out_rights_id, ContentId content_id) { + std::scoped_lock lk(this->mutex); + + /* Attempt to locate the content id in the cache. */ + for (size_t i = 0; i < MaxEntries; i++) { + Entry *entry = &this->entries[i]; + + if (entry->last_accessed != 1 && content_id == entry->uuid) { + entry->last_accessed = this->counter; + this->counter++; + *out_rights_id = entry->rights_id; + return true; + } + } + + return false; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp new file mode 100644 index 000000000..532a79430 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + enum class StorageId : u8 { + None = 0, + Host = 1, + GameCard = 2, + BuiltInSystem = 3, + BuiltInUser = 4, + SdCard = 5, + Any = 6, + }; + + constexpr inline bool IsUniqueStorage(StorageId id) { + return id != StorageId::None && id != StorageId::Any; + } + + constexpr inline bool IsInstallableStorage(StorageId id) { + return id == StorageId::BuiltInSystem || id == StorageId::BuiltInUser || id == StorageId::SdCard || id == StorageId::Any; + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp new file mode 100644 index 000000000..c05ae2a84 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ncm { + + struct SystemProgramId { + u64 value; + + constexpr operator ProgramId() const { + return { this->value }; + } + + static const SystemProgramId Start; + + static const SystemProgramId Fs; + static const SystemProgramId Loader; + static const SystemProgramId Ncm; + static const SystemProgramId Pm; + static const SystemProgramId Sm; + static const SystemProgramId Boot; + static const SystemProgramId Usb; + static const SystemProgramId Tma; + static const SystemProgramId Boot2; + static const SystemProgramId Settings; + static const SystemProgramId Bus; + static const SystemProgramId Bluetooth; + static const SystemProgramId Bcat; + static const SystemProgramId Dmnt; + static const SystemProgramId Friends; + static const SystemProgramId Nifm; + static const SystemProgramId Ptm; + static const SystemProgramId Shell; + static const SystemProgramId BsdSockets; + static const SystemProgramId Hid; + static const SystemProgramId Audio; + static const SystemProgramId LogManager; + static const SystemProgramId Wlan; + static const SystemProgramId Cs; + static const SystemProgramId Ldn; + static const SystemProgramId NvServices; + static const SystemProgramId Pcv; + static const SystemProgramId Ppc; + static const SystemProgramId NvnFlinger; + static const SystemProgramId Pcie; + static const SystemProgramId Account; + static const SystemProgramId Ns; + static const SystemProgramId Nfc; + static const SystemProgramId Psc; + static const SystemProgramId CapSrv; + static const SystemProgramId Am; + static const SystemProgramId Ssl; + static const SystemProgramId Nim; + static const SystemProgramId Cec; + static const SystemProgramId Tspm; + static const SystemProgramId Spl; + static const SystemProgramId Lbl; + static const SystemProgramId Btm; + static const SystemProgramId Erpt; + static const SystemProgramId Time; + static const SystemProgramId Vi; + static const SystemProgramId Pctl; + static const SystemProgramId Npns; + static const SystemProgramId Eupld; + static const SystemProgramId Arp; + static const SystemProgramId Glue; + static const SystemProgramId Eclct; + static const SystemProgramId Es; + static const SystemProgramId Fatal; + static const SystemProgramId Grc; + static const SystemProgramId Creport; + static const SystemProgramId Ro; + static const SystemProgramId Profiler; + static const SystemProgramId Sdb; + static const SystemProgramId Migration; + static const SystemProgramId Jit; + static const SystemProgramId JpegDec; + static const SystemProgramId SafeMode; + static const SystemProgramId Olsc; + static const SystemProgramId Dt; + static const SystemProgramId Nd; + static const SystemProgramId Ngct; + + static const SystemProgramId End; + }; + + struct AtmosphereProgramId { + u64 value; + + constexpr operator SystemProgramId() const { + return { this->value }; + } + + constexpr operator ProgramId() const { + return static_cast(*this); + } + + static const AtmosphereProgramId Mitm; + }; + + inline constexpr const AtmosphereProgramId AtmosphereProgramId::Mitm = { 0x010041544D530000ul }; + + inline constexpr bool IsAtmosphereProgramId(const ProgramId &program_id) { + return program_id == AtmosphereProgramId::Mitm; + } + + inline constexpr bool IsSystemProgramId(const AtmosphereProgramId &program_id) { + return true; + } + + inline constexpr const SystemProgramId SystemProgramId::Start = { 0x0100000000000000ul }; + + inline constexpr const SystemProgramId SystemProgramId::Fs = { 0x0100000000000000ul }; + inline constexpr const SystemProgramId SystemProgramId::Loader = { 0x0100000000000001ul }; + inline constexpr const SystemProgramId SystemProgramId::Ncm = { 0x0100000000000002ul }; + inline constexpr const SystemProgramId SystemProgramId::Pm = { 0x0100000000000003ul }; + inline constexpr const SystemProgramId SystemProgramId::Sm = { 0x0100000000000004ul }; + inline constexpr const SystemProgramId SystemProgramId::Boot = { 0x0100000000000005ul }; + inline constexpr const SystemProgramId SystemProgramId::Usb = { 0x0100000000000006ul }; + inline constexpr const SystemProgramId SystemProgramId::Tma = { 0x0100000000000007ul }; + inline constexpr const SystemProgramId SystemProgramId::Boot2 = { 0x0100000000000008ul }; + inline constexpr const SystemProgramId SystemProgramId::Settings = { 0x0100000000000009ul }; + inline constexpr const SystemProgramId SystemProgramId::Bus = { 0x010000000000000Aul }; + inline constexpr const SystemProgramId SystemProgramId::Bluetooth = { 0x010000000000000Bul }; + inline constexpr const SystemProgramId SystemProgramId::Bcat = { 0x010000000000000Cul }; + inline constexpr const SystemProgramId SystemProgramId::Dmnt = { 0x010000000000000Dul }; + inline constexpr const SystemProgramId SystemProgramId::Friends = { 0x010000000000000Eul }; + inline constexpr const SystemProgramId SystemProgramId::Nifm = { 0x010000000000000Ful }; + inline constexpr const SystemProgramId SystemProgramId::Ptm = { 0x0100000000000010ul }; + inline constexpr const SystemProgramId SystemProgramId::Shell = { 0x0100000000000011ul }; + inline constexpr const SystemProgramId SystemProgramId::BsdSockets = { 0x0100000000000012ul }; + inline constexpr const SystemProgramId SystemProgramId::Hid = { 0x0100000000000013ul }; + inline constexpr const SystemProgramId SystemProgramId::Audio = { 0x0100000000000014ul }; + inline constexpr const SystemProgramId SystemProgramId::LogManager = { 0x0100000000000015ul }; + inline constexpr const SystemProgramId SystemProgramId::Wlan = { 0x0100000000000016ul }; + inline constexpr const SystemProgramId SystemProgramId::Cs = { 0x0100000000000017ul }; + inline constexpr const SystemProgramId SystemProgramId::Ldn = { 0x0100000000000018ul }; + inline constexpr const SystemProgramId SystemProgramId::NvServices = { 0x0100000000000019ul }; + inline constexpr const SystemProgramId SystemProgramId::Pcv = { 0x010000000000001Aul }; + inline constexpr const SystemProgramId SystemProgramId::Ppc = { 0x010000000000001Bul }; + inline constexpr const SystemProgramId SystemProgramId::NvnFlinger = { 0x010000000000001Cul }; + inline constexpr const SystemProgramId SystemProgramId::Pcie = { 0x010000000000001Dul }; + inline constexpr const SystemProgramId SystemProgramId::Account = { 0x010000000000001Eul }; + inline constexpr const SystemProgramId SystemProgramId::Ns = { 0x010000000000001Ful }; + inline constexpr const SystemProgramId SystemProgramId::Nfc = { 0x0100000000000020ul }; + inline constexpr const SystemProgramId SystemProgramId::Psc = { 0x0100000000000021ul }; + inline constexpr const SystemProgramId SystemProgramId::CapSrv = { 0x0100000000000022ul }; + inline constexpr const SystemProgramId SystemProgramId::Am = { 0x0100000000000023ul }; + inline constexpr const SystemProgramId SystemProgramId::Ssl = { 0x0100000000000024ul }; + inline constexpr const SystemProgramId SystemProgramId::Nim = { 0x0100000000000025ul }; + inline constexpr const SystemProgramId SystemProgramId::Cec = { 0x0100000000000026ul }; + inline constexpr const SystemProgramId SystemProgramId::Tspm = { 0x0100000000000027ul }; + inline constexpr const SystemProgramId SystemProgramId::Spl = { 0x0100000000000028ul }; + inline constexpr const SystemProgramId SystemProgramId::Lbl = { 0x0100000000000029ul }; + inline constexpr const SystemProgramId SystemProgramId::Btm = { 0x010000000000002Aul }; + inline constexpr const SystemProgramId SystemProgramId::Erpt = { 0x010000000000002Bul }; + inline constexpr const SystemProgramId SystemProgramId::Time = { 0x010000000000002Cul }; + inline constexpr const SystemProgramId SystemProgramId::Vi = { 0x010000000000002Dul }; + inline constexpr const SystemProgramId SystemProgramId::Pctl = { 0x010000000000002Eul }; + inline constexpr const SystemProgramId SystemProgramId::Npns = { 0x010000000000002Ful }; + inline constexpr const SystemProgramId SystemProgramId::Eupld = { 0x0100000000000030ul }; + inline constexpr const SystemProgramId SystemProgramId::Arp = { 0x0100000000000031ul }; + inline constexpr const SystemProgramId SystemProgramId::Glue = { 0x0100000000000031ul }; + inline constexpr const SystemProgramId SystemProgramId::Eclct = { 0x0100000000000032ul }; + inline constexpr const SystemProgramId SystemProgramId::Es = { 0x0100000000000033ul }; + inline constexpr const SystemProgramId SystemProgramId::Fatal = { 0x0100000000000034ul }; + inline constexpr const SystemProgramId SystemProgramId::Grc = { 0x0100000000000035ul }; + inline constexpr const SystemProgramId SystemProgramId::Creport = { 0x0100000000000036ul }; + inline constexpr const SystemProgramId SystemProgramId::Ro = { 0x0100000000000037ul }; + inline constexpr const SystemProgramId SystemProgramId::Profiler = { 0x0100000000000038ul }; + inline constexpr const SystemProgramId SystemProgramId::Sdb = { 0x0100000000000039ul }; + inline constexpr const SystemProgramId SystemProgramId::Migration = { 0x010000000000003Aul }; + inline constexpr const SystemProgramId SystemProgramId::Jit = { 0x010000000000003Bul }; + inline constexpr const SystemProgramId SystemProgramId::JpegDec = { 0x010000000000003Cul }; + inline constexpr const SystemProgramId SystemProgramId::SafeMode = { 0x010000000000003Dul }; + inline constexpr const SystemProgramId SystemProgramId::Olsc = { 0x010000000000003Eul }; + inline constexpr const SystemProgramId SystemProgramId::Dt = { 0x010000000000003Ful }; + inline constexpr const SystemProgramId SystemProgramId::Nd = { 0x0100000000000040ul }; + inline constexpr const SystemProgramId SystemProgramId::Ngct = { 0x0100000000000041ul }; + + inline constexpr const SystemProgramId SystemProgramId::End = { 0x01000000000007FFul }; + + inline constexpr bool IsSystemProgramId(const ProgramId &program_id) { + return (SystemProgramId::Start <= program_id && program_id <= SystemProgramId::End) || IsAtmosphereProgramId(program_id); + } + + inline constexpr bool IsSystemProgramId(const SystemProgramId &program_id) { + return true; + } + + struct SystemDataId { + u64 value; + + constexpr operator DataId() const { + return { this->value }; + } + + static const SystemDataId Start; + + static const SystemDataId CertStore; + static const SystemDataId ErrorMessage; + static const SystemDataId MiiModel; + static const SystemDataId BrowserDll; + static const SystemDataId Help; + static const SystemDataId SharedFont; + static const SystemDataId NgWord; + static const SystemDataId SsidList; + static const SystemDataId Dictionary; + static const SystemDataId SystemVersion; + static const SystemDataId AvatarImage; + static const SystemDataId LocalNews; + static const SystemDataId Eula; + static const SystemDataId UrlBlackList; + static const SystemDataId TimeZoneBinar; + static const SystemDataId CertStoreCruiser; + static const SystemDataId FontNintendoExtension; + static const SystemDataId FontStandard; + static const SystemDataId FontKorean; + static const SystemDataId FontChineseTraditional; + static const SystemDataId FontChineseSimple; + static const SystemDataId FontBfcpx; + static const SystemDataId SystemUpdate; + + static const SystemDataId FirmwareDebugSettings; + static const SystemDataId BootImagePackage; + static const SystemDataId BootImagePackageSafe; + static const SystemDataId BootImagePackageExFat; + static const SystemDataId BootImagePackageExFatSafe; + static const SystemDataId FatalMessage; + static const SystemDataId ControllerIcon; + static const SystemDataId PlatformConfigIcosa; + static const SystemDataId PlatformConfigCopper; + static const SystemDataId PlatformConfigHoag; + static const SystemDataId ControllerFirmware; + static const SystemDataId NgWord2; + static const SystemDataId PlatformConfigIcosaMariko; + static const SystemDataId ApplicationBlackList; + static const SystemDataId RebootlessSystemUpdateVersion; + static const SystemDataId ContentActionTable; + + static const SystemDataId End; + }; + + inline constexpr const SystemDataId SystemDataId::Start = { 0x0100000000000800ul }; + inline constexpr const SystemDataId SystemDataId::CertStore = { 0x0100000000000800ul }; + inline constexpr const SystemDataId SystemDataId::ErrorMessage = { 0x0100000000000801ul }; + inline constexpr const SystemDataId SystemDataId::MiiModel = { 0x0100000000000802ul }; + inline constexpr const SystemDataId SystemDataId::BrowserDll = { 0x0100000000000803ul }; + inline constexpr const SystemDataId SystemDataId::Help = { 0x0100000000000804ul }; + inline constexpr const SystemDataId SystemDataId::SharedFont = { 0x0100000000000805ul }; + inline constexpr const SystemDataId SystemDataId::NgWord = { 0x0100000000000806ul }; + inline constexpr const SystemDataId SystemDataId::SsidList = { 0x0100000000000807ul }; + inline constexpr const SystemDataId SystemDataId::Dictionary = { 0x0100000000000808ul }; + inline constexpr const SystemDataId SystemDataId::SystemVersion = { 0x0100000000000809ul }; + inline constexpr const SystemDataId SystemDataId::AvatarImage = { 0x010000000000080Aul }; + inline constexpr const SystemDataId SystemDataId::LocalNews = { 0x010000000000080Bul }; + inline constexpr const SystemDataId SystemDataId::Eula = { 0x010000000000080Cul }; + inline constexpr const SystemDataId SystemDataId::UrlBlackList = { 0x010000000000080Dul }; + inline constexpr const SystemDataId SystemDataId::TimeZoneBinar = { 0x010000000000080Eul }; + inline constexpr const SystemDataId SystemDataId::CertStoreCruiser = { 0x010000000000080Ful }; + inline constexpr const SystemDataId SystemDataId::FontNintendoExtension = { 0x0100000000000810ul }; + inline constexpr const SystemDataId SystemDataId::FontStandard = { 0x0100000000000811ul }; + inline constexpr const SystemDataId SystemDataId::FontKorean = { 0x0100000000000812ul }; + inline constexpr const SystemDataId SystemDataId::FontChineseTraditional = { 0x0100000000000813ul }; + inline constexpr const SystemDataId SystemDataId::FontChineseSimple = { 0x0100000000000814ul }; + inline constexpr const SystemDataId SystemDataId::FontBfcpx = { 0x0100000000000815ul }; + inline constexpr const SystemDataId SystemDataId::SystemUpdate = { 0x0100000000000816ul }; + + inline constexpr const SystemDataId SystemDataId::FirmwareDebugSettings = { 0x0100000000000818ul }; + inline constexpr const SystemDataId SystemDataId::BootImagePackage = { 0x0100000000000819ul }; + inline constexpr const SystemDataId SystemDataId::BootImagePackageSafe = { 0x010000000000081Aul }; + inline constexpr const SystemDataId SystemDataId::BootImagePackageExFat = { 0x010000000000081Bul }; + inline constexpr const SystemDataId SystemDataId::BootImagePackageExFatSafe = { 0x010000000000081Cul }; + inline constexpr const SystemDataId SystemDataId::FatalMessage = { 0x010000000000081Dul }; + inline constexpr const SystemDataId SystemDataId::ControllerIcon = { 0x010000000000081Eul }; + inline constexpr const SystemDataId SystemDataId::PlatformConfigIcosa = { 0x010000000000081Ful }; + inline constexpr const SystemDataId SystemDataId::PlatformConfigCopper = { 0x0100000000000820ul }; + inline constexpr const SystemDataId SystemDataId::PlatformConfigHoag = { 0x0100000000000821ul }; + inline constexpr const SystemDataId SystemDataId::ControllerFirmware = { 0x0100000000000822ul }; + inline constexpr const SystemDataId SystemDataId::NgWord2 = { 0x0100000000000823ul }; + inline constexpr const SystemDataId SystemDataId::PlatformConfigIcosaMariko = { 0x0100000000000824ul }; + inline constexpr const SystemDataId SystemDataId::ApplicationBlackList = { 0x0100000000000825ul }; + inline constexpr const SystemDataId SystemDataId::RebootlessSystemUpdateVersion = { 0x0100000000000826ul }; + inline constexpr const SystemDataId SystemDataId::ContentActionTable = { 0x0100000000000827ul }; + + inline constexpr const SystemDataId SystemDataId::End = { 0x0100000000000FFFul }; + + inline constexpr bool IsSystemDataId(const DataId &data_id) { + return SystemDataId::Start <= data_id && data_id <= SystemDataId::End; + } + + inline constexpr bool IsSystemDataId(const SystemDataId &data_id) { + return true; + } + + struct SystemUpdateId { + u64 value; + + constexpr operator DataId() const { + return { this->value }; + } + }; + + struct SystemAppletId { + u64 value; + + constexpr operator ProgramId() const { + return { this->value }; + } + + static const SystemAppletId Start; + + static const SystemAppletId Qlaunch; + static const SystemAppletId Auth; + static const SystemAppletId Cabinet; + static const SystemAppletId Controller; + static const SystemAppletId DataErase; + static const SystemAppletId Error; + static const SystemAppletId NetConnect; + static const SystemAppletId PlayerSelect; + static const SystemAppletId Swkbd; + static const SystemAppletId MiiEdit; + static const SystemAppletId Web; + static const SystemAppletId Shop; + static const SystemAppletId OverlayDisp; + static const SystemAppletId PhotoViewer; + static const SystemAppletId Set; + static const SystemAppletId OfflineWeb; + static const SystemAppletId LoginShare; + static const SystemAppletId WifiWebAuth; + static const SystemAppletId Starter; + static const SystemAppletId MyPage; + static const SystemAppletId PlayReport; + static const SystemAppletId MaintenanceMenu; + + static const SystemAppletId Gift; + static const SystemAppletId DummyShop; + static const SystemAppletId UserMigration; + static const SystemAppletId Encounter; + + static const SystemAppletId Story; + + static const SystemAppletId End; + }; + + inline constexpr const SystemAppletId SystemAppletId::Start = { 0x0100000000001000ul }; + + inline constexpr const SystemAppletId SystemAppletId::Qlaunch = { 0x0100000000001000ul }; + inline constexpr const SystemAppletId SystemAppletId::Auth = { 0x0100000000001001ul }; + inline constexpr const SystemAppletId SystemAppletId::Cabinet = { 0x0100000000001002ul }; + inline constexpr const SystemAppletId SystemAppletId::Controller = { 0x0100000000001003ul }; + inline constexpr const SystemAppletId SystemAppletId::DataErase = { 0x0100000000001004ul }; + inline constexpr const SystemAppletId SystemAppletId::Error = { 0x0100000000001005ul }; + inline constexpr const SystemAppletId SystemAppletId::NetConnect = { 0x0100000000001006ul }; + inline constexpr const SystemAppletId SystemAppletId::PlayerSelect = { 0x0100000000001007ul }; + inline constexpr const SystemAppletId SystemAppletId::Swkbd = { 0x0100000000001008ul }; + inline constexpr const SystemAppletId SystemAppletId::MiiEdit = { 0x0100000000001009ul }; + inline constexpr const SystemAppletId SystemAppletId::Web = { 0x010000000000100Aul }; + inline constexpr const SystemAppletId SystemAppletId::Shop = { 0x010000000000100Bul }; + inline constexpr const SystemAppletId SystemAppletId::OverlayDisp = { 0x010000000000100Cul }; + inline constexpr const SystemAppletId SystemAppletId::PhotoViewer = { 0x010000000000100Dul }; + inline constexpr const SystemAppletId SystemAppletId::Set = { 0x010000000000100Eul }; + inline constexpr const SystemAppletId SystemAppletId::OfflineWeb = { 0x010000000000100Ful }; + inline constexpr const SystemAppletId SystemAppletId::LoginShare = { 0x0100000000001010ul }; + inline constexpr const SystemAppletId SystemAppletId::WifiWebAuth = { 0x0100000000001011ul }; + inline constexpr const SystemAppletId SystemAppletId::Starter = { 0x0100000000001012ul }; + inline constexpr const SystemAppletId SystemAppletId::MyPage = { 0x0100000000001013ul }; + inline constexpr const SystemAppletId SystemAppletId::PlayReport = { 0x0100000000001014ul }; + inline constexpr const SystemAppletId SystemAppletId::MaintenanceMenu = { 0x0100000000001015ul }; + + inline constexpr const SystemAppletId SystemAppletId::Gift = { 0x010000000000101Aul }; + inline constexpr const SystemAppletId SystemAppletId::DummyShop = { 0x010000000000101Bul }; + inline constexpr const SystemAppletId SystemAppletId::UserMigration = { 0x010000000000101Cul }; + inline constexpr const SystemAppletId SystemAppletId::Encounter = { 0x010000000000101Dul }; + + inline constexpr const SystemAppletId SystemAppletId::Story = { 0x0100000000001020ul }; + + inline constexpr const SystemAppletId SystemAppletId::End = { 0x0100000000001FFFul }; + + inline constexpr bool IsSystemAppletId(const ProgramId &program_id) { + return SystemAppletId::Start <= program_id && program_id <= SystemAppletId::End; + } + + inline constexpr bool IsSystemAppletId(const SystemAppletId &program_id) { + return true; + } + + struct LibraryAppletId { + u64 value; + + constexpr operator SystemAppletId() const { + return { this->value }; + } + + constexpr operator ProgramId() const { + return static_cast(*this); + } + + static const LibraryAppletId Auth; + static const LibraryAppletId Controller; + static const LibraryAppletId Error; + static const LibraryAppletId PlayerSelect; + static const LibraryAppletId Swkbd; + static const LibraryAppletId Web; + static const LibraryAppletId Shop; + static const LibraryAppletId PhotoViewer; + static const LibraryAppletId OfflineWeb; + static const LibraryAppletId LoginShare; + static const LibraryAppletId WifiWebAuth; + static const LibraryAppletId MyPage; + + }; + + inline constexpr const LibraryAppletId LibraryAppletId::Auth = { SystemAppletId::Auth.value }; + inline constexpr const LibraryAppletId LibraryAppletId::Controller = { SystemAppletId::Controller.value }; + inline constexpr const LibraryAppletId LibraryAppletId::Error = { SystemAppletId::Error.value }; + inline constexpr const LibraryAppletId LibraryAppletId::PlayerSelect = { SystemAppletId::PlayerSelect.value }; + inline constexpr const LibraryAppletId LibraryAppletId::Swkbd = { SystemAppletId::Swkbd.value }; + inline constexpr const LibraryAppletId LibraryAppletId::Web = { SystemAppletId::Web.value }; + inline constexpr const LibraryAppletId LibraryAppletId::Shop = { SystemAppletId::Shop.value }; + inline constexpr const LibraryAppletId LibraryAppletId::PhotoViewer = { SystemAppletId::PhotoViewer.value }; + inline constexpr const LibraryAppletId LibraryAppletId::OfflineWeb = { SystemAppletId::OfflineWeb.value }; + inline constexpr const LibraryAppletId LibraryAppletId::LoginShare = { SystemAppletId::LoginShare.value }; + inline constexpr const LibraryAppletId LibraryAppletId::WifiWebAuth = { SystemAppletId::WifiWebAuth.value }; + inline constexpr const LibraryAppletId LibraryAppletId::MyPage = { SystemAppletId::MyPage.value }; + + inline constexpr bool IsLibraryAppletId(const ProgramId &id) { + return id == LibraryAppletId::Auth || + id == LibraryAppletId::Controller || + id == LibraryAppletId::Error || + id == LibraryAppletId::PlayerSelect || + id == LibraryAppletId::Swkbd || + id == LibraryAppletId::Web || + id == LibraryAppletId::Shop || + id == LibraryAppletId::PhotoViewer || + id == LibraryAppletId::OfflineWeb || + id == LibraryAppletId::LoginShare || + id == LibraryAppletId::WifiWebAuth || + id == LibraryAppletId::MyPage; + } + + inline constexpr bool IsLibraryAppletId(const LibraryAppletId &id) { + return true; + } + + struct WebAppletId { + u64 value; + + constexpr operator LibraryAppletId() const { + return { this->value }; + } + + constexpr operator SystemAppletId() const { + return static_cast(*this); + } + + constexpr operator ProgramId() const { + return static_cast(*this); + } + + static const WebAppletId Web; + static const WebAppletId Shop; + static const WebAppletId OfflineWeb; + static const WebAppletId LoginShare; + static const WebAppletId WifiWebAuth; + }; + + inline constexpr const WebAppletId WebAppletId::Web = { LibraryAppletId::Web.value }; + inline constexpr const WebAppletId WebAppletId::Shop = { LibraryAppletId::Shop.value }; + inline constexpr const WebAppletId WebAppletId::OfflineWeb = { LibraryAppletId::OfflineWeb.value }; + inline constexpr const WebAppletId WebAppletId::LoginShare = { LibraryAppletId::LoginShare.value }; + inline constexpr const WebAppletId WebAppletId::WifiWebAuth = { LibraryAppletId::WifiWebAuth.value }; + + inline constexpr bool IsWebAppletId(const ProgramId &id) { + return id == WebAppletId::Web || + id == WebAppletId::Shop || + id == WebAppletId::OfflineWeb || + id == WebAppletId::LoginShare || + id == WebAppletId::WifiWebAuth; + } + + inline constexpr bool IsWebAppletId(const WebAppletId &id) { + return true; + } + + struct SystemApplicationId { + u64 value; + + constexpr operator ProgramId() const { + return { this->value }; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp deleted file mode 100644 index 10992f9e6..000000000 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include - -namespace ams::ncm { - - /* Storage IDs. */ - enum class StorageId : u8 { - #define DEFINE_ENUM_MEMBER(nm) nm = NcmStorageId_##nm - DEFINE_ENUM_MEMBER(None), - DEFINE_ENUM_MEMBER(Host), - DEFINE_ENUM_MEMBER(GameCard), - DEFINE_ENUM_MEMBER(BuiltInSystem), - DEFINE_ENUM_MEMBER(BuiltInUser), - DEFINE_ENUM_MEMBER(SdCard), - DEFINE_ENUM_MEMBER(Any), - #undef DEFINE_ENUM_MEMBER - }; - - /* Program IDs (Formerly: Title IDs). */ - struct ProgramId { - svc::ProgramId value; - - inline explicit operator svc::ProgramId() const { - return this->value; - } - - /* Invalid Program ID. */ - static const ProgramId Invalid; - - /* System Modules. */ - static const ProgramId SystemStart; - - static const ProgramId Fs; - static const ProgramId Loader; - static const ProgramId Ncm; - static const ProgramId Pm; - static const ProgramId Sm; - static const ProgramId Boot; - static const ProgramId Usb; - static const ProgramId Tma; - static const ProgramId Boot2; - static const ProgramId Settings; - static const ProgramId Bus; - static const ProgramId Bluetooth; - static const ProgramId Bcat; - static const ProgramId Dmnt; - static const ProgramId Friends; - static const ProgramId Nifm; - static const ProgramId Ptm; - static const ProgramId Shell; - static const ProgramId BsdSockets; - static const ProgramId Hid; - static const ProgramId Audio; - static const ProgramId LogManager; - static const ProgramId Wlan; - static const ProgramId Cs; - static const ProgramId Ldn; - static const ProgramId NvServices; - static const ProgramId Pcv; - static const ProgramId Ppc; - static const ProgramId NvnFlinger; - static const ProgramId Pcie; - static const ProgramId Account; - static const ProgramId Ns; - static const ProgramId Nfc; - static const ProgramId Psc; - static const ProgramId CapSrv; - static const ProgramId Am; - static const ProgramId Ssl; - static const ProgramId Nim; - static const ProgramId Cec; - static const ProgramId Tspm; - static const ProgramId Spl; - static const ProgramId Lbl; - static const ProgramId Btm; - static const ProgramId Erpt; - static const ProgramId Time; - static const ProgramId Vi; - static const ProgramId Pctl; - static const ProgramId Npns; - static const ProgramId Eupld; - static const ProgramId Arp; - static const ProgramId Glue; - static const ProgramId Eclct; - static const ProgramId Es; - static const ProgramId Fatal; - static const ProgramId Grc; - static const ProgramId Creport; - static const ProgramId Ro; - static const ProgramId Profiler; - static const ProgramId Sdb; - static const ProgramId Migration; - static const ProgramId Jit; - static const ProgramId JpegDec; - static const ProgramId SafeMode; - static const ProgramId Olsc; - static const ProgramId Dt; - static const ProgramId Nd; - static const ProgramId Ngct; - - static const ProgramId SystemEnd; - - /* System Data Archives. */ - static const ProgramId ArchiveStart; - static const ProgramId ArchiveCertStore; - static const ProgramId ArchiveErrorMessage; - static const ProgramId ArchiveMiiModel; - static const ProgramId ArchiveBrowserDll; - static const ProgramId ArchiveHelp; - static const ProgramId ArchiveSharedFont; - static const ProgramId ArchiveNgWord; - static const ProgramId ArchiveSsidList; - static const ProgramId ArchiveDictionary; - static const ProgramId ArchiveSystemVersion; - static const ProgramId ArchiveAvatarImage; - static const ProgramId ArchiveLocalNews; - static const ProgramId ArchiveEula; - static const ProgramId ArchiveUrlBlackList; - static const ProgramId ArchiveTimeZoneBinar; - static const ProgramId ArchiveCertStoreCruiser; - static const ProgramId ArchiveFontNintendoExtension; - static const ProgramId ArchiveFontStandard; - static const ProgramId ArchiveFontKorean; - static const ProgramId ArchiveFontChineseTraditional; - static const ProgramId ArchiveFontChineseSimple; - static const ProgramId ArchiveFontBfcpx; - static const ProgramId ArchiveSystemUpdate; - - static const ProgramId ArchiveFirmwareDebugSettings; - static const ProgramId ArchiveBootImagePackage; - static const ProgramId ArchiveBootImagePackageSafe; - static const ProgramId ArchiveBootImagePackageExFat; - static const ProgramId ArchiveBootImagePackageExFatSafe; - static const ProgramId ArchiveFatalMessage; - static const ProgramId ArchiveControllerIcon; - static const ProgramId ArchivePlatformConfigIcosa; - static const ProgramId ArchivePlatformConfigCopper; - static const ProgramId ArchivePlatformConfigHoag; - static const ProgramId ArchiveControllerFirmware; - static const ProgramId ArchiveNgWord2; - static const ProgramId ArchivePlatformConfigIcosaMariko; - static const ProgramId ArchiveApplicationBlackList; - static const ProgramId ArchiveRebootlessSystemUpdateVersion; - static const ProgramId ArchiveContentActionTable; - - static const ProgramId ArchiveEnd; - - /* System Applets. */ - static const ProgramId AppletStart; - - static const ProgramId AppletQlaunch; - static const ProgramId AppletAuth; - static const ProgramId AppletCabinet; - static const ProgramId AppletController; - static const ProgramId AppletDataErase; - static const ProgramId AppletError; - static const ProgramId AppletNetConnect; - static const ProgramId AppletPlayerSelect; - static const ProgramId AppletSwkbd; - static const ProgramId AppletMiiEdit; - static const ProgramId AppletWeb; - static const ProgramId AppletShop; - static const ProgramId AppletOverlayDisp; - static const ProgramId AppletPhotoViewer; - static const ProgramId AppletSet; - static const ProgramId AppletOfflineWeb; - static const ProgramId AppletLoginShare; - static const ProgramId AppletWifiWebAuth; - static const ProgramId AppletStarter; - static const ProgramId AppletMyPage; - static const ProgramId AppletPlayReport; - static const ProgramId AppletMaintenanceMenu; - - static const ProgramId AppletGift; - static const ProgramId AppletDummyShop; - static const ProgramId AppletUserMigration; - static const ProgramId AppletEncounter; - - static const ProgramId AppletStory; - - static const ProgramId AppletEnd; - - /* Debug Applets. */ - - /* Debug Modules. */ - - /* Factory Setup. */ - - /* Applications. */ - static const ProgramId ApplicationStart; - static const ProgramId ApplicationEnd; - - /* Atmosphere Extensions. */ - static const ProgramId AtmosphereMitm; - }; - - /* Invalid Program ID. */ - inline constexpr const ProgramId ProgramId::Invalid = {}; - - inline constexpr const ProgramId InvalidProgramId = ProgramId::Invalid; - - /* System Modules. */ - inline constexpr const ProgramId ProgramId::SystemStart = { 0x0100000000000000ul }; - - inline constexpr const ProgramId ProgramId::Fs = { 0x0100000000000000ul }; - inline constexpr const ProgramId ProgramId::Loader = { 0x0100000000000001ul }; - inline constexpr const ProgramId ProgramId::Ncm = { 0x0100000000000002ul }; - inline constexpr const ProgramId ProgramId::Pm = { 0x0100000000000003ul }; - inline constexpr const ProgramId ProgramId::Sm = { 0x0100000000000004ul }; - inline constexpr const ProgramId ProgramId::Boot = { 0x0100000000000005ul }; - inline constexpr const ProgramId ProgramId::Usb = { 0x0100000000000006ul }; - inline constexpr const ProgramId ProgramId::Tma = { 0x0100000000000007ul }; - inline constexpr const ProgramId ProgramId::Boot2 = { 0x0100000000000008ul }; - inline constexpr const ProgramId ProgramId::Settings = { 0x0100000000000009ul }; - inline constexpr const ProgramId ProgramId::Bus = { 0x010000000000000Aul }; - inline constexpr const ProgramId ProgramId::Bluetooth = { 0x010000000000000Bul }; - inline constexpr const ProgramId ProgramId::Bcat = { 0x010000000000000Cul }; - inline constexpr const ProgramId ProgramId::Dmnt = { 0x010000000000000Dul }; - inline constexpr const ProgramId ProgramId::Friends = { 0x010000000000000Eul }; - inline constexpr const ProgramId ProgramId::Nifm = { 0x010000000000000Ful }; - inline constexpr const ProgramId ProgramId::Ptm = { 0x0100000000000010ul }; - inline constexpr const ProgramId ProgramId::Shell = { 0x0100000000000011ul }; - inline constexpr const ProgramId ProgramId::BsdSockets = { 0x0100000000000012ul }; - inline constexpr const ProgramId ProgramId::Hid = { 0x0100000000000013ul }; - inline constexpr const ProgramId ProgramId::Audio = { 0x0100000000000014ul }; - inline constexpr const ProgramId ProgramId::LogManager = { 0x0100000000000015ul }; - inline constexpr const ProgramId ProgramId::Wlan = { 0x0100000000000016ul }; - inline constexpr const ProgramId ProgramId::Cs = { 0x0100000000000017ul }; - inline constexpr const ProgramId ProgramId::Ldn = { 0x0100000000000018ul }; - inline constexpr const ProgramId ProgramId::NvServices = { 0x0100000000000019ul }; - inline constexpr const ProgramId ProgramId::Pcv = { 0x010000000000001Aul }; - inline constexpr const ProgramId ProgramId::Ppc = { 0x010000000000001Bul }; - inline constexpr const ProgramId ProgramId::NvnFlinger = { 0x010000000000001Cul }; - inline constexpr const ProgramId ProgramId::Pcie = { 0x010000000000001Dul }; - inline constexpr const ProgramId ProgramId::Account = { 0x010000000000001Eul }; - inline constexpr const ProgramId ProgramId::Ns = { 0x010000000000001Ful }; - inline constexpr const ProgramId ProgramId::Nfc = { 0x0100000000000020ul }; - inline constexpr const ProgramId ProgramId::Psc = { 0x0100000000000021ul }; - inline constexpr const ProgramId ProgramId::CapSrv = { 0x0100000000000022ul }; - inline constexpr const ProgramId ProgramId::Am = { 0x0100000000000023ul }; - inline constexpr const ProgramId ProgramId::Ssl = { 0x0100000000000024ul }; - inline constexpr const ProgramId ProgramId::Nim = { 0x0100000000000025ul }; - inline constexpr const ProgramId ProgramId::Cec = { 0x0100000000000026ul }; - inline constexpr const ProgramId ProgramId::Tspm = { 0x0100000000000027ul }; - inline constexpr const ProgramId ProgramId::Spl = { 0x0100000000000028ul }; - inline constexpr const ProgramId ProgramId::Lbl = { 0x0100000000000029ul }; - inline constexpr const ProgramId ProgramId::Btm = { 0x010000000000002Aul }; - inline constexpr const ProgramId ProgramId::Erpt = { 0x010000000000002Bul }; - inline constexpr const ProgramId ProgramId::Time = { 0x010000000000002Cul }; - inline constexpr const ProgramId ProgramId::Vi = { 0x010000000000002Dul }; - inline constexpr const ProgramId ProgramId::Pctl = { 0x010000000000002Eul }; - inline constexpr const ProgramId ProgramId::Npns = { 0x010000000000002Ful }; - inline constexpr const ProgramId ProgramId::Eupld = { 0x0100000000000030ul }; - inline constexpr const ProgramId ProgramId::Arp = { 0x0100000000000031ul }; - inline constexpr const ProgramId ProgramId::Glue = { 0x0100000000000031ul }; - inline constexpr const ProgramId ProgramId::Eclct = { 0x0100000000000032ul }; - inline constexpr const ProgramId ProgramId::Es = { 0x0100000000000033ul }; - inline constexpr const ProgramId ProgramId::Fatal = { 0x0100000000000034ul }; - inline constexpr const ProgramId ProgramId::Grc = { 0x0100000000000035ul }; - inline constexpr const ProgramId ProgramId::Creport = { 0x0100000000000036ul }; - inline constexpr const ProgramId ProgramId::Ro = { 0x0100000000000037ul }; - inline constexpr const ProgramId ProgramId::Profiler = { 0x0100000000000038ul }; - inline constexpr const ProgramId ProgramId::Sdb = { 0x0100000000000039ul }; - inline constexpr const ProgramId ProgramId::Migration = { 0x010000000000003Aul }; - inline constexpr const ProgramId ProgramId::Jit = { 0x010000000000003Bul }; - inline constexpr const ProgramId ProgramId::JpegDec = { 0x010000000000003Cul }; - inline constexpr const ProgramId ProgramId::SafeMode = { 0x010000000000003Dul }; - inline constexpr const ProgramId ProgramId::Olsc = { 0x010000000000003Eul }; - inline constexpr const ProgramId ProgramId::Dt = { 0x010000000000003Ful }; - inline constexpr const ProgramId ProgramId::Nd = { 0x0100000000000040ul }; - inline constexpr const ProgramId ProgramId::Ngct = { 0x0100000000000041ul }; - - inline constexpr const ProgramId ProgramId::SystemEnd = { 0x01000000000007FFul }; - - /* System Data Archives. */ - inline constexpr const ProgramId ProgramId::ArchiveStart = { 0x0100000000000800ul }; - inline constexpr const ProgramId ProgramId::ArchiveCertStore = { 0x0100000000000800ul }; - inline constexpr const ProgramId ProgramId::ArchiveErrorMessage = { 0x0100000000000801ul }; - inline constexpr const ProgramId ProgramId::ArchiveMiiModel = { 0x0100000000000802ul }; - inline constexpr const ProgramId ProgramId::ArchiveBrowserDll = { 0x0100000000000803ul }; - inline constexpr const ProgramId ProgramId::ArchiveHelp = { 0x0100000000000804ul }; - inline constexpr const ProgramId ProgramId::ArchiveSharedFont = { 0x0100000000000805ul }; - inline constexpr const ProgramId ProgramId::ArchiveNgWord = { 0x0100000000000806ul }; - inline constexpr const ProgramId ProgramId::ArchiveSsidList = { 0x0100000000000807ul }; - inline constexpr const ProgramId ProgramId::ArchiveDictionary = { 0x0100000000000808ul }; - inline constexpr const ProgramId ProgramId::ArchiveSystemVersion = { 0x0100000000000809ul }; - inline constexpr const ProgramId ProgramId::ArchiveAvatarImage = { 0x010000000000080Aul }; - inline constexpr const ProgramId ProgramId::ArchiveLocalNews = { 0x010000000000080Bul }; - inline constexpr const ProgramId ProgramId::ArchiveEula = { 0x010000000000080Cul }; - inline constexpr const ProgramId ProgramId::ArchiveUrlBlackList = { 0x010000000000080Dul }; - inline constexpr const ProgramId ProgramId::ArchiveTimeZoneBinar = { 0x010000000000080Eul }; - inline constexpr const ProgramId ProgramId::ArchiveCertStoreCruiser = { 0x010000000000080Ful }; - inline constexpr const ProgramId ProgramId::ArchiveFontNintendoExtension = { 0x0100000000000810ul }; - inline constexpr const ProgramId ProgramId::ArchiveFontStandard = { 0x0100000000000811ul }; - inline constexpr const ProgramId ProgramId::ArchiveFontKorean = { 0x0100000000000812ul }; - inline constexpr const ProgramId ProgramId::ArchiveFontChineseTraditional = { 0x0100000000000813ul }; - inline constexpr const ProgramId ProgramId::ArchiveFontChineseSimple = { 0x0100000000000814ul }; - inline constexpr const ProgramId ProgramId::ArchiveFontBfcpx = { 0x0100000000000815ul }; - inline constexpr const ProgramId ProgramId::ArchiveSystemUpdate = { 0x0100000000000816ul }; - - inline constexpr const ProgramId ProgramId::ArchiveFirmwareDebugSettings = { 0x0100000000000818ul }; - inline constexpr const ProgramId ProgramId::ArchiveBootImagePackage = { 0x0100000000000819ul }; - inline constexpr const ProgramId ProgramId::ArchiveBootImagePackageSafe = { 0x010000000000081Aul }; - inline constexpr const ProgramId ProgramId::ArchiveBootImagePackageExFat = { 0x010000000000081Bul }; - inline constexpr const ProgramId ProgramId::ArchiveBootImagePackageExFatSafe = { 0x010000000000081Cul }; - inline constexpr const ProgramId ProgramId::ArchiveFatalMessage = { 0x010000000000081Dul }; - inline constexpr const ProgramId ProgramId::ArchiveControllerIcon = { 0x010000000000081Eul }; - inline constexpr const ProgramId ProgramId::ArchivePlatformConfigIcosa = { 0x010000000000081Ful }; - inline constexpr const ProgramId ProgramId::ArchivePlatformConfigCopper = { 0x0100000000000820ul }; - inline constexpr const ProgramId ProgramId::ArchivePlatformConfigHoag = { 0x0100000000000821ul }; - inline constexpr const ProgramId ProgramId::ArchiveControllerFirmware = { 0x0100000000000822ul }; - inline constexpr const ProgramId ProgramId::ArchiveNgWord2 = { 0x0100000000000823ul }; - inline constexpr const ProgramId ProgramId::ArchivePlatformConfigIcosaMariko = { 0x0100000000000824ul }; - inline constexpr const ProgramId ProgramId::ArchiveApplicationBlackList = { 0x0100000000000825ul }; - inline constexpr const ProgramId ProgramId::ArchiveRebootlessSystemUpdateVersion = { 0x0100000000000826ul }; - inline constexpr const ProgramId ProgramId::ArchiveContentActionTable = { 0x0100000000000827ul }; - - inline constexpr const ProgramId ProgramId::ArchiveEnd = { 0x0100000000000FFFul }; - - /* System Applets. */ - inline constexpr const ProgramId ProgramId::AppletStart = { 0x0100000000001000ul }; - - inline constexpr const ProgramId ProgramId::AppletQlaunch = { 0x0100000000001000ul }; - inline constexpr const ProgramId ProgramId::AppletAuth = { 0x0100000000001001ul }; - inline constexpr const ProgramId ProgramId::AppletCabinet = { 0x0100000000001002ul }; - inline constexpr const ProgramId ProgramId::AppletController = { 0x0100000000001003ul }; - inline constexpr const ProgramId ProgramId::AppletDataErase = { 0x0100000000001004ul }; - inline constexpr const ProgramId ProgramId::AppletError = { 0x0100000000001005ul }; - inline constexpr const ProgramId ProgramId::AppletNetConnect = { 0x0100000000001006ul }; - inline constexpr const ProgramId ProgramId::AppletPlayerSelect = { 0x0100000000001007ul }; - inline constexpr const ProgramId ProgramId::AppletSwkbd = { 0x0100000000001008ul }; - inline constexpr const ProgramId ProgramId::AppletMiiEdit = { 0x0100000000001009ul }; - inline constexpr const ProgramId ProgramId::AppletWeb = { 0x010000000000100Aul }; - inline constexpr const ProgramId ProgramId::AppletShop = { 0x010000000000100Bul }; - inline constexpr const ProgramId ProgramId::AppletOverlayDisp = { 0x010000000000100Cul }; - inline constexpr const ProgramId ProgramId::AppletPhotoViewer = { 0x010000000000100Dul }; - inline constexpr const ProgramId ProgramId::AppletSet = { 0x010000000000100Eul }; - inline constexpr const ProgramId ProgramId::AppletOfflineWeb = { 0x010000000000100Ful }; - inline constexpr const ProgramId ProgramId::AppletLoginShare = { 0x0100000000001010ul }; - inline constexpr const ProgramId ProgramId::AppletWifiWebAuth = { 0x0100000000001011ul }; - inline constexpr const ProgramId ProgramId::AppletStarter = { 0x0100000000001012ul }; - inline constexpr const ProgramId ProgramId::AppletMyPage = { 0x0100000000001013ul }; - inline constexpr const ProgramId ProgramId::AppletPlayReport = { 0x0100000000001014ul }; - inline constexpr const ProgramId ProgramId::AppletMaintenanceMenu = { 0x0100000000001015ul }; - - inline constexpr const ProgramId ProgramId::AppletGift = { 0x010000000000101Aul }; - inline constexpr const ProgramId ProgramId::AppletDummyShop = { 0x010000000000101Bul }; - inline constexpr const ProgramId ProgramId::AppletUserMigration = { 0x010000000000101Cul }; - inline constexpr const ProgramId ProgramId::AppletEncounter = { 0x010000000000101Dul }; - - inline constexpr const ProgramId ProgramId::AppletStory = { 0x0100000000001020ul }; - - inline constexpr const ProgramId ProgramId::AppletEnd = { 0x0100000000001FFFul }; - - /* Debug Applets. */ - - /* Debug Modules. */ - - /* Factory Setup. */ - - /* Applications. */ - inline constexpr const ProgramId ProgramId::ApplicationStart = { 0x0100000000010000ul }; - inline constexpr const ProgramId ProgramId::ApplicationEnd = { 0x01FFFFFFFFFFFFFFul }; - - /* Atmosphere Extensions. */ - inline constexpr const ProgramId ProgramId::AtmosphereMitm = { 0x010041544D530000ul }; - - inline constexpr bool operator==(const ProgramId &lhs, const ProgramId &rhs) { - return lhs.value == rhs.value; - } - - inline constexpr bool operator!=(const ProgramId &lhs, const ProgramId &rhs) { - return lhs.value != rhs.value; - } - - inline constexpr bool operator<(const ProgramId &lhs, const ProgramId &rhs) { - return lhs.value < rhs.value; - } - - inline constexpr bool operator<=(const ProgramId &lhs, const ProgramId &rhs) { - return lhs.value <= rhs.value; - } - - inline constexpr bool operator>(const ProgramId &lhs, const ProgramId &rhs) { - return lhs.value > rhs.value; - } - - inline constexpr bool operator>=(const ProgramId &lhs, const ProgramId &rhs) { - return lhs.value >= rhs.value; - } - - inline constexpr bool IsSystemProgramId(const ProgramId &program_id) { - return ProgramId::SystemStart <= program_id && program_id <= ProgramId::SystemEnd; - } - - inline constexpr bool IsArchiveProgramId(const ProgramId &program_id) { - return ProgramId::ArchiveStart <= program_id && program_id <= ProgramId::ArchiveEnd; - } - - inline constexpr bool IsAppletProgramId(const ProgramId &program_id) { - return ProgramId::AppletStart <= program_id && program_id <= ProgramId::AppletEnd; - } - - inline constexpr bool IsApplicationProgramId(const ProgramId &program_id) { - return ProgramId::ApplicationStart <= program_id && program_id <= ProgramId::ApplicationEnd; - } - - inline constexpr bool IsWebAppletProgramId(const ProgramId &program_id) { - return program_id == ProgramId::AppletWeb || - program_id == ProgramId::AppletShop || - program_id == ProgramId::AppletOfflineWeb || - program_id == ProgramId::AppletLoginShare || - program_id == ProgramId::AppletWifiWebAuth; - } - - static_assert(sizeof(ProgramId) == sizeof(u64) && std::is_pod::value, "ProgramId definition!"); - - /* Program Location. */ - struct ProgramLocation { - ProgramId program_id; - u8 storage_id; - - static constexpr ProgramLocation Make(ProgramId program_id, StorageId storage_id) { - return { .program_id = program_id, .storage_id = static_cast(storage_id), }; - } - }; - - static_assert(sizeof(ProgramLocation) == 0x10 && std::is_pod::value, "ProgramLocation definition!"); - static_assert(sizeof(ProgramLocation) == sizeof(::NcmProgramLocation) && alignof(ProgramLocation) == alignof(::NcmProgramLocation), "ProgramLocation Libnx Compatibility"); - -} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp index bf1d53a7e..d2b05bd96 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp @@ -30,9 +30,7 @@ namespace ams::os { private: CondVar cv; public: - ConditionVariable() { - condvarInit(&cv); - } + constexpr ConditionVariable() : cv() { /* ... */ } ConditionVariableStatus TimedWait(::Mutex *m, u64 timeout) { if (timeout > 0) { diff --git a/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp b/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp index a1f3db374..5fdec6d43 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp @@ -28,13 +28,11 @@ namespace ams::os { private: ::Mutex m; private: - ::Mutex *GetMutex() { + constexpr ::Mutex *GetMutex() { return &this->m; } public: - Mutex() { - mutexInit(GetMutex()); - } + constexpr Mutex() : m() { /* ... */ } void lock() { mutexLock(GetMutex()); @@ -65,13 +63,11 @@ namespace ams::os { private: ::RMutex m; private: - ::RMutex *GetMutex() { + constexpr ::RMutex *GetMutex() { return &this->m; } public: - RecursiveMutex() { - rmutexInit(GetMutex()); - } + constexpr RecursiveMutex() : m() { /* ... */ } void lock() { rmutexLock(GetMutex()); diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_dmnt_api.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_dmnt_api.hpp index 78492e883..e37998e9a 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_dmnt_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_dmnt_api.hpp @@ -16,8 +16,9 @@ #pragma once -#include "../ldr.hpp" -#include "pm_types.hpp" +#include +#include +#include namespace ams::pm::dmnt { diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_info_api.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_info_api.hpp index 374c5f1d0..940621f05 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_info_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_info_api.hpp @@ -16,8 +16,11 @@ #pragma once -#include "pm_types.hpp" -#include "../ncm/ncm_types.hpp" +#include +#include +#include +#include +#include namespace ams::pm::info { diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp index fde8a89b1..9464b01b5 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp @@ -16,8 +16,9 @@ #pragma once -#include "../ldr.hpp" -#include "pm_types.hpp" +#include +#include +#include namespace ams::pm::shell { diff --git a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp index 44856cc34..eafde873c 100644 --- a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp @@ -16,7 +16,7 @@ #pragma once #include -#include "../ncm/ncm_types.hpp" +#include namespace ams::ro { diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index 1f347ca40..3ceb664d8 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -86,11 +86,13 @@ namespace ams::sf { cmif::ServiceObjectHolder *srv; cmif::DomainObjectId *object_id; public: + Out(cmif::ServiceObjectHolder *s) : srv(s), object_id(nullptr) { /* ... */ } Out(cmif::ServiceObjectHolder *s, cmif::DomainObjectId *o) : srv(s), object_id(o) { /* ... */ } void SetValue(std::shared_ptr &&s, cmif::DomainObjectId new_object_id = cmif::InvalidDomainObjectId) { *this->srv = cmif::ServiceObjectHolder(std::move(s)); if (new_object_id != cmif::InvalidDomainObjectId) { + AMS_ABORT_UNLESS(object_id != nullptr); *this->object_id = new_object_id; } } diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_manager_api.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_manager_api.hpp index a01086b6b..6531923ab 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_manager_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_manager_api.hpp @@ -15,10 +15,10 @@ */ #pragma once - -#include "sm_types.hpp" -#include "../ncm/ncm_types.hpp" -#include "../os/os_common_types.hpp" +#include +#include +#include +#include namespace ams::sm::manager { diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp index a0cd09e0f..ecc9e1629 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp @@ -65,7 +65,12 @@ namespace ams::sm { AMS_ABORT_UNLESS(!this->has_initialized); sm::DoWithSession([&]() { - this->result = Initializer(); + if constexpr (std::is_same::value) { + Initializer(); + this->result = ResultSuccess(); + } else { + this->result = Initializer(); + } }); this->has_initialized = R_SUCCEEDED(this->result); diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp index 983925a0c..3a9dc531d 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp @@ -16,8 +16,8 @@ #pragma once #include -#include "../ncm/ncm_types.hpp" -#include "../cfg/cfg_types.hpp" +#include +#include namespace ams::sm { diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 7b1ca0bc3..e73e66713 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -65,7 +65,7 @@ namespace ams::spl { constexpr inline ::ams::Result ConvertResult(Result smc_result) { /* smc::Result::Success becomes ResultSuccess() directly. */ - R_UNLESS(smc_result != Result::Success, ResultSuccess()); + R_SUCCEED_IF(smc_result == smc::Result::Success); /* Convert to the list of known SecureMonitorErrors. */ const auto converted = R_MAKE_NAMESPACE_RESULT(::ams::spl, static_cast(smc_result)); diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index 14a594772..ded458209 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include #include namespace ams::boot2 { @@ -24,99 +23,99 @@ namespace ams::boot2 { /* psc, bus, pcv is the minimal set of required programs to get SD card. */ /* bus depends on pcie, and pcv depends on settings. */ - constexpr ncm::ProgramId PreSdCardLaunchPrograms[] = { - ncm::ProgramId::Psc, /* psc */ - ncm::ProgramId::Pcie, /* pcie */ - ncm::ProgramId::Bus, /* bus */ - ncm::ProgramId::Settings, /* settings */ - ncm::ProgramId::Pcv, /* pcv */ - ncm::ProgramId::Usb, /* usb */ + constexpr ncm::SystemProgramId PreSdCardLaunchPrograms[] = { + ncm::SystemProgramId::Psc, /* psc */ + ncm::SystemProgramId::Pcie, /* pcie */ + ncm::SystemProgramId::Bus, /* bus */ + ncm::SystemProgramId::Settings, /* settings */ + ncm::SystemProgramId::Pcv, /* pcv */ + ncm::SystemProgramId::Usb, /* usb */ }; constexpr size_t NumPreSdCardLaunchPrograms = util::size(PreSdCardLaunchPrograms); - constexpr ncm::ProgramId AdditionalLaunchPrograms[] = { - ncm::ProgramId::Tma, /* tma */ - ncm::ProgramId::Am, /* am */ - ncm::ProgramId::NvServices, /* nvservices */ - ncm::ProgramId::NvnFlinger, /* nvnflinger */ - ncm::ProgramId::Vi, /* vi */ - ncm::ProgramId::Ns, /* ns */ - ncm::ProgramId::LogManager, /* lm */ - ncm::ProgramId::Ppc, /* ppc */ - ncm::ProgramId::Ptm, /* ptm */ - ncm::ProgramId::Hid, /* hid */ - ncm::ProgramId::Audio, /* audio */ - ncm::ProgramId::Lbl, /* lbl */ - ncm::ProgramId::Wlan, /* wlan */ - ncm::ProgramId::Bluetooth, /* bluetooth */ - ncm::ProgramId::BsdSockets, /* bsdsockets */ - ncm::ProgramId::Nifm, /* nifm */ - ncm::ProgramId::Ldn, /* ldn */ - ncm::ProgramId::Account, /* account */ - ncm::ProgramId::Friends, /* friends */ - ncm::ProgramId::Nfc, /* nfc */ - ncm::ProgramId::JpegDec, /* jpegdec */ - ncm::ProgramId::CapSrv, /* capsrv */ - ncm::ProgramId::Ssl, /* ssl */ - ncm::ProgramId::Nim, /* nim */ - ncm::ProgramId::Bcat, /* bcat */ - ncm::ProgramId::Erpt, /* erpt */ - ncm::ProgramId::Es, /* es */ - ncm::ProgramId::Pctl, /* pctl */ - ncm::ProgramId::Btm, /* btm */ - ncm::ProgramId::Eupld, /* eupld */ - ncm::ProgramId::Glue, /* glue */ - /* ncm::ProgramId::Eclct, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ - ncm::ProgramId::Npns, /* npns */ - ncm::ProgramId::Fatal, /* fatal */ - ncm::ProgramId::Ro, /* ro */ - ncm::ProgramId::Profiler, /* profiler */ - ncm::ProgramId::Sdb, /* sdb */ - ncm::ProgramId::Migration, /* migration */ - ncm::ProgramId::Grc, /* grc */ - ncm::ProgramId::Olsc, /* olsc */ - ncm::ProgramId::Ngct, /* ngct */ + constexpr ncm::SystemProgramId AdditionalLaunchPrograms[] = { + ncm::SystemProgramId::Tma, /* tma */ + ncm::SystemProgramId::Am, /* am */ + ncm::SystemProgramId::NvServices, /* nvservices */ + ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ + ncm::SystemProgramId::Vi, /* vi */ + ncm::SystemProgramId::Ns, /* ns */ + ncm::SystemProgramId::LogManager, /* lm */ + ncm::SystemProgramId::Ppc, /* ppc */ + ncm::SystemProgramId::Ptm, /* ptm */ + ncm::SystemProgramId::Hid, /* hid */ + ncm::SystemProgramId::Audio, /* audio */ + ncm::SystemProgramId::Lbl, /* lbl */ + ncm::SystemProgramId::Wlan, /* wlan */ + ncm::SystemProgramId::Bluetooth, /* bluetooth */ + ncm::SystemProgramId::BsdSockets, /* bsdsockets */ + ncm::SystemProgramId::Nifm, /* nifm */ + ncm::SystemProgramId::Ldn, /* ldn */ + ncm::SystemProgramId::Account, /* account */ + ncm::SystemProgramId::Friends, /* friends */ + ncm::SystemProgramId::Nfc, /* nfc */ + ncm::SystemProgramId::JpegDec, /* jpegdec */ + ncm::SystemProgramId::CapSrv, /* capsrv */ + ncm::SystemProgramId::Ssl, /* ssl */ + ncm::SystemProgramId::Nim, /* nim */ + ncm::SystemProgramId::Bcat, /* bcat */ + ncm::SystemProgramId::Erpt, /* erpt */ + ncm::SystemProgramId::Es, /* es */ + ncm::SystemProgramId::Pctl, /* pctl */ + ncm::SystemProgramId::Btm, /* btm */ + ncm::SystemProgramId::Eupld, /* eupld */ + ncm::SystemProgramId::Glue, /* glue */ + /* ncm::SystemProgramId::Eclct, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ + ncm::SystemProgramId::Npns, /* npns */ + ncm::SystemProgramId::Fatal, /* fatal */ + ncm::SystemProgramId::Ro, /* ro */ + ncm::SystemProgramId::Profiler, /* profiler */ + ncm::SystemProgramId::Sdb, /* sdb */ + ncm::SystemProgramId::Migration, /* migration */ + ncm::SystemProgramId::Grc, /* grc */ + ncm::SystemProgramId::Olsc, /* olsc */ + ncm::SystemProgramId::Ngct, /* ngct */ }; constexpr size_t NumAdditionalLaunchPrograms = util::size(AdditionalLaunchPrograms); - constexpr ncm::ProgramId AdditionalMaintenanceLaunchPrograms[] = { - ncm::ProgramId::Tma, /* tma */ - ncm::ProgramId::Am, /* am */ - ncm::ProgramId::NvServices, /* nvservices */ - ncm::ProgramId::NvnFlinger, /* nvnflinger */ - ncm::ProgramId::Vi, /* vi */ - ncm::ProgramId::Ns, /* ns */ - ncm::ProgramId::LogManager, /* lm */ - ncm::ProgramId::Ppc, /* ppc */ - ncm::ProgramId::Ptm, /* ptm */ - ncm::ProgramId::Hid, /* hid */ - ncm::ProgramId::Audio, /* audio */ - ncm::ProgramId::Lbl, /* lbl */ - ncm::ProgramId::Wlan, /* wlan */ - ncm::ProgramId::Bluetooth, /* bluetooth */ - ncm::ProgramId::BsdSockets, /* bsdsockets */ - ncm::ProgramId::Nifm, /* nifm */ - ncm::ProgramId::Ldn, /* ldn */ - ncm::ProgramId::Account, /* account */ - ncm::ProgramId::Nfc, /* nfc */ - ncm::ProgramId::JpegDec, /* jpegdec */ - ncm::ProgramId::CapSrv, /* capsrv */ - ncm::ProgramId::Ssl, /* ssl */ - ncm::ProgramId::Nim, /* nim */ - ncm::ProgramId::Erpt, /* erpt */ - ncm::ProgramId::Es, /* es */ - ncm::ProgramId::Pctl, /* pctl */ - ncm::ProgramId::Btm, /* btm */ - ncm::ProgramId::Glue, /* glue */ - /* ncm::ProgramId::Eclct, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ - ncm::ProgramId::Fatal, /* fatal */ - ncm::ProgramId::Ro, /* ro */ - ncm::ProgramId::Profiler, /* profiler */ - ncm::ProgramId::Sdb, /* sdb */ - ncm::ProgramId::Migration, /* migration */ - ncm::ProgramId::Grc, /* grc */ - ncm::ProgramId::Olsc, /* olsc */ - ncm::ProgramId::Ngct, /* ngct */ + constexpr ncm::SystemProgramId AdditionalMaintenanceLaunchPrograms[] = { + ncm::SystemProgramId::Tma, /* tma */ + ncm::SystemProgramId::Am, /* am */ + ncm::SystemProgramId::NvServices, /* nvservices */ + ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ + ncm::SystemProgramId::Vi, /* vi */ + ncm::SystemProgramId::Ns, /* ns */ + ncm::SystemProgramId::LogManager, /* lm */ + ncm::SystemProgramId::Ppc, /* ppc */ + ncm::SystemProgramId::Ptm, /* ptm */ + ncm::SystemProgramId::Hid, /* hid */ + ncm::SystemProgramId::Audio, /* audio */ + ncm::SystemProgramId::Lbl, /* lbl */ + ncm::SystemProgramId::Wlan, /* wlan */ + ncm::SystemProgramId::Bluetooth, /* bluetooth */ + ncm::SystemProgramId::BsdSockets, /* bsdsockets */ + ncm::SystemProgramId::Nifm, /* nifm */ + ncm::SystemProgramId::Ldn, /* ldn */ + ncm::SystemProgramId::Account, /* account */ + ncm::SystemProgramId::Nfc, /* nfc */ + ncm::SystemProgramId::JpegDec, /* jpegdec */ + ncm::SystemProgramId::CapSrv, /* capsrv */ + ncm::SystemProgramId::Ssl, /* ssl */ + ncm::SystemProgramId::Nim, /* nim */ + ncm::SystemProgramId::Erpt, /* erpt */ + ncm::SystemProgramId::Es, /* es */ + ncm::SystemProgramId::Pctl, /* pctl */ + ncm::SystemProgramId::Btm, /* btm */ + ncm::SystemProgramId::Glue, /* glue */ + /* ncm::SystemProgramId::Eclct, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ + ncm::SystemProgramId::Fatal, /* fatal */ + ncm::SystemProgramId::Ro, /* ro */ + ncm::SystemProgramId::Profiler, /* profiler */ + ncm::SystemProgramId::Sdb, /* sdb */ + ncm::SystemProgramId::Migration, /* migration */ + ncm::SystemProgramId::Grc, /* grc */ + ncm::SystemProgramId::Olsc, /* olsc */ + ncm::SystemProgramId::Ngct, /* ngct */ }; constexpr size_t NumAdditionalMaintenanceLaunchPrograms = util::size(AdditionalMaintenanceLaunchPrograms); @@ -150,7 +149,7 @@ namespace ams::boot2 { } } - void LaunchList(const ncm::ProgramId *launch_list, size_t num_entries) { + void LaunchList(const ncm::SystemProgramId *launch_list, size_t num_entries) { for (size_t i = 0; i < num_entries; i++) { LaunchProgram(nullptr, ncm::ProgramLocation::Make(launch_list[i], ncm::StorageId::BuiltInSystem), 0); } @@ -191,19 +190,20 @@ namespace ams::boot2 { template void IterateOverFlaggedProgramsOnSdCard(F f) { /* Validate that the contents directory exists. */ - DIR *contents_dir = opendir("sdmc:/atmosphere/contents"); - if (contents_dir == nullptr) { + fs::DirectoryHandle contents_dir; + if (R_FAILED(fs::OpenDirectory(&contents_dir, "sdmc:/atmosphere/contents", fs::OpenDirectoryMode_Directory))) { return; } - ON_SCOPE_EXIT { closedir(contents_dir); }; + ON_SCOPE_EXIT { fs::CloseDirectory(contents_dir); }; /* Iterate over entries in the contents directory */ - struct dirent *ent; - while ((ent = readdir(contents_dir)) != nullptr) { + fs::DirectoryEntry entry; + s64 count; + while (R_SUCCEEDED(fs::ReadDirectory(&count, &entry, contents_dir, 1)) && count == 1) { /* Check that the subdirectory can be converted to a program id. */ - if (std::strlen(ent->d_name) == 2 * sizeof(ncm::ProgramId) && IsHexadecimal(ent->d_name)) { + if (std::strlen(entry.name) == 2 * sizeof(ncm::ProgramId) && IsHexadecimal(entry.name)) { /* Check if we've already launched the program. */ - ncm::ProgramId program_id{std::strtoul(ent->d_name, nullptr, 16)}; + ncm::ProgramId program_id{std::strtoul(entry.name, nullptr, 16)}; /* Check if the program is flagged. */ if (!cfg::HasContentSpecificFlag(program_id, "boot2")) { @@ -224,14 +224,16 @@ namespace ams::boot2 { /* Read the mitm list off the SD card. */ { - char path[FS_MAX_PATH]; - std::snprintf(mitm_list, sizeof(mitm_list), "sdmc:/atmosphere/contents/%016lx/mitm.lst", static_cast(program_id)); - FILE *f = fopen(path, "rb"); - if (f == nullptr) { + char path[fs::EntryNameLengthMax]; + std::snprintf(path, sizeof(path), "sdmc:/atmosphere/contents/%016lx/mitm.lst", static_cast(program_id)); + + fs::FileHandle f; + if (R_FAILED(fs::OpenFile(&f, path, fs::OpenMode_Read))) { return; } - mitm_list_size = static_cast(fread(mitm_list, 1, sizeof(mitm_list), f)); - fclose(f); + ON_SCOPE_EXIT { fs::CloseFile(f); }; + + R_ABORT_UNLESS(fs::ReadFile(&mitm_list_size, f, 0, mitm_list, sizeof(mitm_list))); } /* Validate read size. */ @@ -313,7 +315,7 @@ namespace ams::boot2 { } /* Launch Atmosphere boot2, using NcmStorageId_None to force SD card boot. */ - LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::ProgramId::Boot2, ncm::StorageId::None), 0); + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Boot2, ncm::StorageId::None), 0); } void LaunchPostSdCardBootPrograms() { @@ -326,7 +328,7 @@ namespace ams::boot2 { } /* Launch Atmosphere dmnt, using NcmStorageId_None to force SD card boot. */ - LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::ProgramId::Dmnt, ncm::StorageId::None), 0); + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0); /* Check for and forward declare non-atmosphere mitm modules. */ DetectAndDeclareFutureMitms(); @@ -336,7 +338,7 @@ namespace ams::boot2 { LaunchList(AdditionalMaintenanceLaunchPrograms, NumAdditionalMaintenanceLaunchPrograms); /* Starting in 7.0.0, npns is launched during maintenance boot. */ if (hos::GetVersion() >= hos::Version_700) { - LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::ProgramId::Npns, ncm::StorageId::BuiltInSystem), 0); + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Npns, ncm::StorageId::BuiltInSystem), 0); } } else { LaunchList(AdditionalLaunchPrograms, NumAdditionalLaunchPrograms); diff --git a/libraries/libstratosphere/source/cfg/cfg_override.cpp b/libraries/libstratosphere/source/cfg/cfg_override.cpp index e1159b82c..f12e5193c 100644 --- a/libraries/libstratosphere/source/cfg/cfg_override.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_override.cpp @@ -38,7 +38,7 @@ namespace ams::cfg { .key_combination = KEY_R, .override_by_default = true, }, - .program_id = ncm::ProgramId::AppletPhotoViewer, + .program_id = ncm::SystemAppletId::PhotoViewer, }; constexpr size_t MaxProgramOverrideKeys = 8; @@ -269,7 +269,7 @@ namespace ams::cfg { } inline bool IsAnyApplicationHblProgramId(ncm::ProgramId program_id) { - return g_hbl_override_config.override_any_app && ncm::IsApplicationProgramId(program_id) && !IsAnySpecificHblProgramId(program_id); + return g_hbl_override_config.override_any_app && ncm::IsApplicationId(program_id) && !IsAnySpecificHblProgramId(program_id); } void ParseIniFile(util::ini::Handler handler, const char *path, void *user_ctx) { @@ -320,7 +320,7 @@ namespace ams::cfg { } /* For system modules and anything launched before the home menu, always override. */ - if (program_id < ncm::ProgramId::AppletStart || !pm::info::HasLaunchedProgram(ncm::ProgramId::AppletQlaunch)) { + if (program_id < ncm::SystemAppletId::Start || !pm::info::HasLaunchedProgram(ncm::SystemAppletId::Qlaunch)) { status.SetProgramSpecific(); return status; } diff --git a/libraries/libstratosphere/source/fs/fs_bis.cpp b/libraries/libstratosphere/source/fs/fs_bis.cpp new file mode 100644 index 000000000..a90ee12b8 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_bis.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + namespace { + + class BisCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable { + private: + const BisPartitionId id; + public: + explicit BisCommonMountNameGenerator(BisPartitionId i) : id(i) { /* ... */ } + + virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override { + /* Determine how much space we need. */ + const char *bis_mount_name = GetBisMountName(this->id); + const size_t needed_size = strnlen(bis_mount_name, MountNameLengthMax) + 2; + AMS_ABORT_UNLESS(dst_size >= needed_size); + + /* Generate the name. */ + auto size = std::snprintf(dst, dst_size, "%s:", bis_mount_name); + AMS_ASSERT(static_cast(size) == needed_size - 1); + + return ResultSuccess(); + } + }; + + } + + namespace impl { + + Result MountBisImpl(const char *name, BisPartitionId id, const char *root_path) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountNameAllowingReserved(name)); + + /* Open the partition. This uses libnx bindings. */ + /* Note: Nintendo ignores the root_path here. */ + FsFileSystem fs; + R_TRY(fsOpenBisFileSystem(std::addressof(fs), static_cast<::FsBisPartitionId>(id), "")); + + /* Allocate a new mountname generator. */ + auto generator = std::make_unique(id); + R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInBisA()); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInBisB()); + + /* Register. */ + return fsa::Register(name, std::move(fsa), std::move(generator)); + } + + Result SetBisRootForHostImpl(BisPartitionId id, const char *root_path) { + /* Ensure the path isn't too long. */ + size_t len = strnlen(root_path, fs::EntryNameLengthMax + 1); + R_UNLESS(len <= fs::EntryNameLengthMax, fs::ResultTooLongPath()); + + fssrv::sf::Path sf_path; + if (len > 0) { + const bool ending_sep = PathTool::IsSeparator(root_path[len - 1]); + FspPathPrintf(std::addressof(sf_path), "%s%s", root_path, ending_sep ? "" : "/"); + } else { + sf_path.str[0] = '\x00'; + } + + /* TODO: Libnx binding for fsSetBisRootForHost */ + AMS_ABORT(); + } + + } + + const char *GetBisMountName(BisPartitionId id) { + switch (id) { + case BisPartitionId::CalibrationFile: return impl::BisCalibrationFilePartitionMountName; + case BisPartitionId::SafeMode: return impl::BisSafeModePartitionMountName; + case BisPartitionId::User: return impl::BisUserPartitionMountName; + case BisPartitionId::System: return impl::BisSystemPartitionMountName; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + Result MountBis(BisPartitionId id, const char *root_path) { + return impl::MountBisImpl(GetBisMountName(id), id, root_path); + } + + Result MountBis(const char *name, BisPartitionId id) { + return impl::MountBisImpl(name, id, nullptr); + } + + void SetBisRootForHost(BisPartitionId id, const char *root_path) { + R_ABORT_UNLESS(impl::SetBisRootForHostImpl(id, root_path)); + } + + Result OpenBisPartition(std::unique_ptr *out, BisPartitionId id) { + /* Open the partition. This uses libnx bindings. */ + FsStorage s; + R_TRY(fsOpenBisStorage(std::addressof(s), static_cast<::FsBisPartitionId>(id))); + + /* Allocate a new storage wrapper. */ + auto storage = std::make_unique(s); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInBisC()); + + *out = std::move(storage); + return ResultSuccess(); + } + + Result InvalidateBisCache() { + /* TODO: Libnx binding for this command. */ + AMS_ABORT(); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_code.cpp b/libraries/libstratosphere/source/fs/fs_code.cpp new file mode 100644 index 000000000..83e9b5c5d --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_code.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + Result MountCode(const char *name, const char *path, ncm::ProgramId program_id) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Validate the path isn't null. */ + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + + /* Print a path suitable for the remove service. */ + fssrv::sf::Path sf_path; + R_TRY(FspPathPrintf(std::addressof(sf_path), "%s", path)); + + /* Open the filesystem using libnx bindings. */ + ::FsFileSystem fs; + R_TRY(fsldrOpenCodeFileSystem(program_id.value, sf_path.str, std::addressof(fs))); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA()); + + /* Register. */ + return fsa::Register(name, std::move(fsa)); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_content.cpp b/libraries/libstratosphere/source/fs/fs_content.cpp new file mode 100644 index 000000000..fc14c9602 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_content.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + namespace { + + impl::FileSystemProxyType ConvertToFileSystemProxyType(ContentType content_type) { + switch (content_type) { + case ContentType_Control: return impl::FileSystemProxyType_Control; + case ContentType_Manual: return impl::FileSystemProxyType_Manual; + case ContentType_Meta: return impl::FileSystemProxyType_Meta; + case ContentType_Data: return impl::FileSystemProxyType_Data; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + Result MountContentImpl(const char *name, const char *path, u64 id, impl::FileSystemProxyType type) { + /* Open a filesystem using libnx bindings. */ + FsFileSystem fs; + R_TRY(fsOpenFileSystemWithId(std::addressof(fs), id, static_cast<::FsFileSystemType>(type), path)); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentA()); + + /* Register. */ + return fsa::Register(name, std::move(fsa)); + } + + Result MountContent(const char *name, const char *path, u64 id, ContentType content_type) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountNameAllowingReserved(name)); + + /* Validate the path. */ + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + + /* Mount the content. */ + return MountContentImpl(name, path, id, ConvertToFileSystemProxyType(content_type)); + } + + } + + Result MountContent(const char *name, const char *path, ContentType content_type) { + /* This API only supports mounting Meta content. */ + AMS_ABORT_UNLESS(content_type == ContentType_Meta); + + return MountContent(name, path, ncm::InvalidProgramId, content_type); + } + + Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type) { + return MountContent(name, path, id.value, content_type); + } + + Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type) { + return MountContent(name, path, id.value, content_type); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_content_storage.cpp b/libraries/libstratosphere/source/fs/fs_content_storage.cpp new file mode 100644 index 000000000..5e778eb3c --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_content_storage.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + namespace { + + class ContentStorageCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable { + private: + const ContentStorageId id; + public: + explicit ContentStorageCommonMountNameGenerator(ContentStorageId i) : id(i) { /* ... */ } + + virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override { + /* Determine how much space we need. */ + const size_t needed_size = strnlen(GetContentStorageMountName(id), MountNameLengthMax) + 2; + AMS_ABORT_UNLESS(dst_size >= needed_size); + + /* Generate the name. */ + auto size = std::snprintf(dst, dst_size, "%s:", GetContentStorageMountName(id)); + AMS_ASSERT(static_cast(size) == needed_size - 1); + + return ResultSuccess(); + } + }; + + } + + const char *GetContentStorageMountName(ContentStorageId id) { + switch (id) { + case ContentStorageId::System: return impl::ContentStorageSystemMountName; + case ContentStorageId::User: return impl::ContentStorageUserMountName; + case ContentStorageId::SdCard: return impl::ContentStorageSdCardMountName; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + Result MountContentStorage(ContentStorageId id) { + return MountContentStorage(GetContentStorageMountName(id), id); + } + + Result MountContentStorage(const char *name, ContentStorageId id) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountNameAllowingReserved(name)); + + /* It can take some time for the system partition to be ready (if it's on the SD card). */ + /* Thus, we will retry up to 10 times, waiting one second each time. */ + constexpr size_t MaxRetries = 10; + constexpr u64 RetryInterval = 1'000'000'000ul; + + /* Mount the content storage, use libnx bindings. */ + ::FsFileSystem fs; + for (size_t i = 0; i < MaxRetries; i++) { + R_TRY_CATCH(fsOpenContentStorageFileSystem(std::addressof(fs), static_cast<::FsContentStorageId>(id))) { + R_CATCH(fs::ResultSystemPartitionNotReady) { + if (i < MaxRetries - 1) { + /* TODO: os::SleepThread */ + svcSleepThread(RetryInterval); + } else { + return fs::ResultSystemPartitionNotReady(); + } + } + } R_END_TRY_CATCH; + } + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA()); + + /* Allocate a new mountname generator. */ + auto generator = std::make_unique(id); + R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInContentStorageB()); + + /* Register. */ + return fsa::Register(name, std::move(fsa), std::move(generator)); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_data.cpp b/libraries/libstratosphere/source/fs/fs_data.cpp new file mode 100644 index 000000000..150097d67 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_data.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs::impl { + + namespace { + + Result OpenDataStorageByDataId(std::unique_ptr *out, ncm::DataId data_id, ncm::StorageId storage_id) { + /* Open storage using libnx bindings. */ + ::FsStorage s; + R_TRY_CATCH(fsOpenDataStorageByDataId(std::addressof(s), data_id.value, static_cast<::NcmStorageId>(storage_id))) { + R_CONVERT(ncm::ResultContentMetaNotFound, fs::ResultTargetNotFound()); + } R_END_TRY_CATCH; + + auto storage = std::make_unique(s); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInDataA()); + + *out = std::move(storage); + return ResultSuccess(); + } + + Result MountDataImpl(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_cache, bool use_data_cache, bool use_path_cache) { + std::unique_ptr storage; + R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id)); + + auto fs = std::make_unique(); + R_UNLESS(fs != nullptr, fs::ResultAllocationFailureInDataB()); + R_TRY(fs->Initialize(std::move(storage), cache_buffer, cache_size, use_cache)); + + return fsa::Register(name, std::move(fs), nullptr, use_data_cache, use_path_cache, false); + } + + } + + Result QueryMountDataCacheSize(size_t *out, ncm::DataId data_id, ncm::StorageId storage_id) { + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + + std::unique_ptr storage; + R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id)); + + size_t size = 0; + R_TRY(RomFsFileSystem::GetRequiredWorkingMemorySize(std::addressof(size), storage.get())); + + constexpr size_t MinimumCacheSize = 32; + *out = std::max(size, MinimumCacheSize); + return ResultSuccess(); + } + + Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + return MountDataImpl(name, data_id, storage_id, nullptr, 0, false, false, false); + } + + Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument()); + + return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, false, false); + } + + Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_data_cache, bool use_path_cache) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument()); + + return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, use_data_cache, use_path_cache); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_dbm_hierarchical_rom_file_table.cpp b/libraries/libstratosphere/source/fs/fs_dbm_hierarchical_rom_file_table.cpp new file mode 100644 index 000000000..b0067efcd --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_dbm_hierarchical_rom_file_table.cpp @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::fs { + + s64 HierarchicalRomFileTable::QueryDirectoryEntryStorageSize(u32 count) { + const size_t real_count = count + ReservedDirectoryCount; + return DirectoryEntryMapTable::QueryKeyValueStorageSize(real_count) + real_count * (RomPathTool::MaxPathLength + 1) * sizeof(RomPathChar); + } + + s64 HierarchicalRomFileTable::QueryDirectoryEntryBucketStorageSize(s64 count) { + return DirectoryEntryMapTable::QueryBucketStorageSize(count); + } + + s64 HierarchicalRomFileTable::QueryFileEntryStorageSize(u32 count) { + return FileEntryMapTable::QueryKeyValueStorageSize(count) + count * (RomPathTool::MaxPathLength + 1) * sizeof(RomPathChar); + } + + s64 HierarchicalRomFileTable::QueryFileEntryBucketStorageSize(s64 count) { + return FileEntryMapTable::QueryBucketStorageSize(count); + } + + Result HierarchicalRomFileTable::Format(SubStorage dir_bucket, SubStorage file_bucket) { + s64 dir_bucket_size; + R_TRY(dir_bucket.GetSize(std::addressof(dir_bucket_size))); + R_TRY(DirectoryEntryMapTable::Format(dir_bucket, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size))); + + s64 file_bucket_size; + R_TRY(file_bucket.GetSize(std::addressof(file_bucket_size))); + R_TRY(FileEntryMapTable::Format(file_bucket, FileEntryMapTable::QueryBucketCount(file_bucket_size))); + + return ResultSuccess(); + } + + HierarchicalRomFileTable::HierarchicalRomFileTable() { /* ... */ } + + Result HierarchicalRomFileTable::Initialize(SubStorage dir_bucket, SubStorage dir_entry, SubStorage file_bucket, SubStorage file_entry) { + s64 dir_bucket_size; + R_TRY(dir_bucket.GetSize(std::addressof(dir_bucket_size))); + R_TRY(this->dir_table.Initialize(dir_bucket, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size), dir_entry)); + + s64 file_bucket_size; + R_TRY(file_bucket.GetSize(std::addressof(file_bucket_size))); + R_TRY(this->file_table.Initialize(file_bucket, FileEntryMapTable::QueryBucketCount(file_bucket_size), file_entry)); + + return ResultSuccess(); + } + + void HierarchicalRomFileTable::Finalize() { + this->dir_table.Finalize(); + this->file_table.Finalize(); + } + + Result HierarchicalRomFileTable::CreateRootDirectory() { + Position root_pos = RootPosition; + EntryKey root_key = {}; + root_key.key.parent = root_pos; + RomPathTool::InitializeRomEntryName(std::addressof(root_key.name)); + RomDirectoryEntry root_entry = { + .next = InvalidPosition, + .dir = InvalidPosition, + .file = InvalidPosition, + }; + return this->dir_table.Add(std::addressof(root_pos), root_key, root_entry); + } + + Result HierarchicalRomFileTable::CreateDirectory(RomDirectoryId *out, const RomPathChar *path, const DirectoryInfo &info) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(path != nullptr); + + RomDirectoryEntry parent_entry = {}; + EntryKey new_key = {}; + R_TRY(this->FindDirectoryRecursive(std::addressof(new_key), std::addressof(parent_entry), path)); + + R_TRY(this->CheckSameEntryExists(new_key, fs::ResultDbmAlreadyExists())); + + RomDirectoryEntry new_entry = { + .next = InvalidPosition, + .dir = InvalidPosition, + .file = InvalidPosition, + }; + + Position new_pos = 0; + R_TRY_CATCH(this->dir_table.Add(std::addressof(new_pos), new_key, new_entry)) { + R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmDirectoryEntryFull()) + } R_END_TRY_CATCH; + + *out = ConvertToDirectoryId(new_pos); + + if (parent_entry.dir == InvalidPosition) { + parent_entry.dir = new_pos; + + R_TRY(this->dir_table.SetByPosition(new_key.key.parent, parent_entry)); + } else { + Position cur_pos = parent_entry.dir; + while (true) { + RomEntryKey cur_key = {}; + RomDirectoryEntry cur_entry = {}; + R_TRY(this->dir_table.GetByPosition(std::addressof(cur_key), std::addressof(cur_entry), cur_pos)); + + if (cur_entry.next == InvalidPosition) { + cur_entry.next = new_pos; + + R_TRY(this->dir_table.SetByPosition(cur_pos, cur_entry)); + break; + } + + cur_pos = cur_entry.next; + } + } + + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::CreateFile(RomFileId *out, const RomPathChar *path, const FileInfo &info) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(path != nullptr); + + RomDirectoryEntry parent_entry = {}; + EntryKey new_key = {}; + R_TRY(this->FindFileRecursive(std::addressof(new_key), std::addressof(parent_entry), path)); + + R_TRY(this->CheckSameEntryExists(new_key, fs::ResultDbmAlreadyExists())); + + RomFileEntry new_entry = { + .next = InvalidPosition, + .info = info, + }; + + Position new_pos = 0; + R_TRY_CATCH(this->file_table.Add(std::addressof(new_pos), new_key, new_entry)) { + R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmFileEntryFull()) + } R_END_TRY_CATCH; + + *out = ConvertToFileId(new_pos); + + if (parent_entry.file == InvalidPosition) { + parent_entry.file = new_pos; + + R_TRY(this->dir_table.SetByPosition(new_key.key.parent, parent_entry)); + } else { + Position cur_pos = parent_entry.file; + while (true) { + RomEntryKey cur_key = {}; + RomFileEntry cur_entry = {}; + R_TRY(this->file_table.GetByPosition(std::addressof(cur_key), std::addressof(cur_entry), cur_pos)); + + if (cur_entry.next == InvalidPosition) { + cur_entry.next = new_pos; + + R_TRY(this->file_table.SetByPosition(cur_pos, cur_entry)); + break; + } + + cur_pos = cur_entry.next; + } + } + + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::ConvertPathToDirectoryId(RomDirectoryId *out, const RomPathChar *path) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(path != nullptr); + + RomDirectoryEntry parent_entry = {}; + EntryKey key = {}; + R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path)); + + Position pos = 0; + RomDirectoryEntry entry = {}; + R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key)); + + *out = ConvertToDirectoryId(pos); + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::ConvertPathToFileId(RomFileId *out, const RomPathChar *path) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(path != nullptr); + + RomDirectoryEntry parent_entry = {}; + EntryKey key = {}; + R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path)); + + Position pos = 0; + RomFileEntry entry = {}; + R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key)); + + *out = ConvertToFileId(pos); + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::GetDirectoryInformation(DirectoryInfo *out, const RomPathChar *path) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(path != nullptr); + + RomDirectoryEntry parent_entry = {}; + EntryKey key = {}; + R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path)); + + return this->GetDirectoryInformation(out, key); + } + + Result HierarchicalRomFileTable::GetDirectoryInformation(DirectoryInfo *out, RomDirectoryId id) { + AMS_ASSERT(out != nullptr); + + RomDirectoryEntry entry = {}; + R_TRY(this->GetDirectoryEntry(std::addressof(entry), id)); + + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::OpenFile(FileInfo *out, const RomPathChar *path) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(path != nullptr); + + RomDirectoryEntry parent_entry = {}; + EntryKey key = {}; + R_TRY(this->FindFileRecursive(std::addressof(key), std::addressof(parent_entry), path)); + + return this->OpenFile(out, key); + } + + Result HierarchicalRomFileTable::OpenFile(FileInfo *out, RomFileId id) { + AMS_ASSERT(out != nullptr); + + RomFileEntry entry = {}; + R_TRY(this->GetFileEntry(std::addressof(entry), id)); + + *out = entry.info; + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::FindOpen(FindPosition *out, const RomPathChar *path) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(path != nullptr); + + RomDirectoryEntry parent_entry = {}; + EntryKey key = {}; + R_TRY(this->FindDirectoryRecursive(std::addressof(key), std::addressof(parent_entry), path)); + + return this->FindOpen(out, key); + } + + Result HierarchicalRomFileTable::FindOpen(FindPosition *out, RomDirectoryId id) { + AMS_ASSERT(out != nullptr); + + out->next_dir = InvalidPosition; + out->next_file = InvalidPosition; + + RomDirectoryEntry entry = {}; + R_TRY(this->GetDirectoryEntry(std::addressof(entry), id)); + + out->next_dir = entry.dir; + out->next_file = entry.file; + + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::FindNextDirectory(RomPathChar *out, FindPosition *find, size_t length) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(find != nullptr); + AMS_ASSERT(length > RomPathTool::MaxPathLength); + + R_UNLESS(find->next_dir != InvalidPosition, fs::ResultDbmFindFinished()); + + RomEntryKey key = {}; + RomDirectoryEntry entry = {}; + size_t aux_size = 0; + R_TRY(this->dir_table.GetByPosition(std::addressof(key), std::addressof(entry), out, std::addressof(aux_size), find->next_dir)); + AMS_ASSERT(aux_size / sizeof(RomPathChar) <= RomPathTool::MaxPathLength); + + out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator; + + find->next_dir = entry.next; + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::FindNextFile(RomPathChar *out, FindPosition *find, size_t length) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(find != nullptr); + AMS_ASSERT(length > RomPathTool::MaxPathLength); + + R_UNLESS(find->next_file != InvalidPosition, fs::ResultDbmFindFinished()); + + RomEntryKey key = {}; + RomFileEntry entry = {}; + size_t aux_size = 0; + R_TRY(this->file_table.GetByPosition(std::addressof(key), std::addressof(entry), out, std::addressof(aux_size), find->next_file)); + AMS_ASSERT(aux_size / sizeof(RomPathChar) <= RomPathTool::MaxPathLength); + + out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator; + + find->next_file = entry.next; + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::QueryRomFileSystemSize(s64 *out_dir_entry_size, s64 *out_file_entry_size) { + AMS_ASSERT(out_dir_entry_size != nullptr); + AMS_ASSERT(out_file_entry_size != nullptr); + + *out_dir_entry_size = this->dir_table.GetTotalEntrySize(); + *out_file_entry_size = this->file_table.GetTotalEntrySize(); + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::GetGrandParent(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, Position pos, RomPathTool::RomEntryName name, const RomPathChar *path) { + AMS_ASSERT(out_pos != nullptr); + AMS_ASSERT(out_dir_key != nullptr); + AMS_ASSERT(out_dir_entry != nullptr); + AMS_ASSERT(path != nullptr); + + RomEntryKey gp_key = {}; + RomDirectoryEntry gp_entry = {}; + R_TRY(this->dir_table.GetByPosition(std::addressof(gp_key), std::addressof(gp_entry), pos)); + out_dir_key->key.parent = gp_key.parent; + + R_TRY(RomPathTool::GetParentDirectoryName(std::addressof(out_dir_key->name), name, path)); + + R_TRY(this->GetDirectoryEntry(out_pos, out_dir_entry, *out_dir_key)); + + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::FindParentDirectoryRecursive(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, RomPathTool::PathParser *parser, const RomPathChar *path) { + AMS_ASSERT(out_pos != nullptr); + AMS_ASSERT(out_dir_key != nullptr); + AMS_ASSERT(out_dir_entry != nullptr); + AMS_ASSERT(parser != nullptr); + AMS_ASSERT(path != nullptr); + + Position dir_pos = RootPosition; + EntryKey dir_key = {}; + RomDirectoryEntry dir_entry = {}; + dir_key.key.parent = RootPosition; + + R_TRY(parser->GetNextDirectoryName(std::addressof(dir_key.name))); + R_TRY(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key)); + + Position parent_pos = dir_pos; + while (!parser->IsFinished()) { + EntryKey old_key = dir_key; + + R_TRY(parser->GetNextDirectoryName(std::addressof(dir_key.name))); + + if (RomPathTool::IsCurrentDirectory(dir_key.name)) { + dir_key = old_key; + continue; + } else if (RomPathTool::IsParentDirectory(dir_key.name)) { + R_UNLESS(parent_pos != RootPosition, fs::ResultDirectoryUnobtainable()); + + R_TRY(this->GetGrandParent(std::addressof(parent_pos), std::addressof(dir_key), std::addressof(dir_entry), dir_key.key.parent, dir_key.name, path)); + } else { + dir_key.key.parent = parent_pos; + R_TRY_CATCH(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key)) { + R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultDbmNotFound()) + } R_END_TRY_CATCH; + + parent_pos = dir_pos; + } + } + + *out_pos = parent_pos; + *out_dir_key = dir_key; + *out_dir_entry = dir_entry; + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::FindPathRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, bool is_dir, const RomPathChar *path) { + AMS_ASSERT(out_key != nullptr); + AMS_ASSERT(out_dir_entry != nullptr); + AMS_ASSERT(path != nullptr); + + RomPathTool::PathParser parser; + R_TRY(parser.Initialize(path)); + + EntryKey parent_key = {}; + Position parent_pos = 0; + R_TRY(this->FindParentDirectoryRecursive(std::addressof(parent_pos), std::addressof(parent_key), out_dir_entry, std::addressof(parser), path)); + + if (is_dir) { + RomPathTool::RomEntryName name = {}; + R_TRY(parser.GetAsDirectoryName(std::addressof(name))); + + if (RomPathTool::IsCurrentDirectory(name)) { + *out_key = parent_key; + if (out_key->key.parent != RootPosition) { + Position pos = 0; + R_TRY(this->GetGrandParent(std::addressof(pos), std::addressof(parent_key), out_dir_entry, out_key->key.parent, out_key->name, path)); + } + } else if (RomPathTool::IsParentDirectory(name)) { + R_UNLESS(parent_pos != RootPosition, fs::ResultDirectoryUnobtainable()); + + Position pos = 0; + RomDirectoryEntry cur_entry = {}; + R_TRY(this->GetGrandParent(std::addressof(pos), out_key, std::addressof(cur_entry), parent_key.key.parent, parent_key.name, path)); + + if (out_key->key.parent != RootPosition) { + R_TRY(this->GetGrandParent(std::addressof(pos), std::addressof(parent_key), out_dir_entry, out_key->key.parent, out_key->name, path)); + } + } else { + out_key->name = name; + out_key->key.parent = (out_key->name.length > 0) ? parent_pos : RootPosition; + } + } else { + { + RomPathTool::RomEntryName name = {}; + R_TRY(parser.GetAsDirectoryName(std::addressof(name))); + R_UNLESS(!RomPathTool::IsParentDirectory(name) || parent_pos != RootPosition, fs::ResultDirectoryUnobtainable()); + } + + R_UNLESS(!parser.IsDirectoryPath(), fs::ResultDbmInvalidOperation()); + + out_key->key.parent = parent_pos; + R_TRY(parser.GetAsFileName(std::addressof(out_key->name))); + } + + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::FindDirectoryRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path) { + AMS_ASSERT(out_key != nullptr); + AMS_ASSERT(out_dir_entry != nullptr); + AMS_ASSERT(path != nullptr); + + return this->FindPathRecursive(out_key, out_dir_entry, true, path); + } + + Result HierarchicalRomFileTable::FindFileRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path) { + AMS_ASSERT(out_key != nullptr); + AMS_ASSERT(out_dir_entry != nullptr); + AMS_ASSERT(path != nullptr); + + return this->FindPathRecursive(out_key, out_dir_entry, false, path); + } + + Result HierarchicalRomFileTable::CheckSameEntryExists(const EntryKey &key, Result if_exists) { + /* Check dir */ + { + Position pos = InvalidPosition; + RomDirectoryEntry entry = {}; + const Result get_res = this->dir_table.Get(std::addressof(pos), std::addressof(entry), key); + if (!fs::ResultDbmKeyNotFound::Includes(get_res)) { + R_TRY(get_res); + return if_exists; + } + } + + /* Check file */ + { + Position pos = InvalidPosition; + RomFileEntry entry = {}; + const Result get_res = this->file_table.Get(std::addressof(pos), std::addressof(entry), key); + if (!fs::ResultDbmKeyNotFound::Includes(get_res)) { + R_TRY(get_res); + return if_exists; + } + } + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::GetDirectoryEntry(Position *out_pos, RomDirectoryEntry *out_entry, const EntryKey &key) { + AMS_ASSERT(out_pos != nullptr); + AMS_ASSERT(out_entry != nullptr); + + const Result dir_res = this->dir_table.Get(out_pos, out_entry, key); + R_UNLESS(R_FAILED(dir_res), dir_res); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), dir_res); + + Position pos = 0; + RomFileEntry entry = {}; + const Result file_res = this->file_table.Get(std::addressof(pos), std::addressof(entry), key); + R_UNLESS(R_FAILED(file_res), fs::ResultDbmInvalidOperation()); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), fs::ResultDbmDirectoryNotFound()); + return file_res; + } + + Result HierarchicalRomFileTable::GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id) { + AMS_ASSERT(out_entry != nullptr); + Position pos = ConvertToPosition(id); + + RomEntryKey key = {}; + const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), out_entry, pos); + R_UNLESS(R_FAILED(dir_res), dir_res); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), dir_res); + + RomFileEntry entry = {}; + const Result file_res = this->file_table.GetByPosition(std::addressof(key), std::addressof(entry), pos); + R_UNLESS(R_FAILED(file_res), fs::ResultDbmInvalidOperation()); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), fs::ResultDbmDirectoryNotFound()); + return file_res; + } + + Result HierarchicalRomFileTable::GetFileEntry(Position *out_pos, RomFileEntry *out_entry, const EntryKey &key) { + AMS_ASSERT(out_pos != nullptr); + AMS_ASSERT(out_entry != nullptr); + + const Result file_res = this->file_table.Get(out_pos, out_entry, key); + R_UNLESS(R_FAILED(file_res), file_res); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), file_res); + + Position pos = 0; + RomDirectoryEntry entry = {}; + const Result dir_res = this->dir_table.Get(std::addressof(pos), std::addressof(entry), key); + R_UNLESS(R_FAILED(dir_res), fs::ResultDbmInvalidOperation()); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), fs::ResultDbmFileNotFound()); + return dir_res; + } + + Result HierarchicalRomFileTable::GetFileEntry(RomFileEntry *out_entry, RomFileId id) { + AMS_ASSERT(out_entry != nullptr); + Position pos = ConvertToPosition(id); + + RomEntryKey key = {}; + const Result file_res = this->file_table.GetByPosition(std::addressof(key), out_entry, pos); + R_UNLESS(R_FAILED(file_res), file_res); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), file_res); + + RomDirectoryEntry entry = {}; + const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), std::addressof(entry), pos); + R_UNLESS(R_FAILED(dir_res), fs::ResultDbmInvalidOperation()); + R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), fs::ResultDbmFileNotFound()); + return dir_res; + } + + Result HierarchicalRomFileTable::GetDirectoryInformation(DirectoryInfo *out, const EntryKey &key) { + AMS_ASSERT(out != nullptr); + + Position pos = 0; + RomDirectoryEntry entry = {}; + R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key)); + + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::OpenFile(FileInfo *out, const EntryKey &key) { + AMS_ASSERT(out != nullptr); + + Position pos = 0; + RomFileEntry entry = {}; + R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key)); + + *out = entry.info; + return ResultSuccess(); + } + + Result HierarchicalRomFileTable::FindOpen(FindPosition *out, const EntryKey &key) { + AMS_ASSERT(out != nullptr); + + out->next_dir = InvalidPosition; + out->next_file = InvalidPosition; + + Position pos = 0; + RomDirectoryEntry entry = {}; + R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key)); + + out->next_dir = entry.dir; + out->next_file = entry.file; + + return ResultSuccess(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/fs/fs_dbm_rom_path_tool.cpp b/libraries/libstratosphere/source/fs/fs_dbm_rom_path_tool.cpp new file mode 100644 index 000000000..a5f10379c --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_dbm_rom_path_tool.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::fs::RomPathTool { + + Result PathParser::Initialize(const RomPathChar *path) { + AMS_ASSERT(path != nullptr); + + /* Require paths start with a separator, and skip repeated separators. */ + R_UNLESS(IsSeparator(path[0]), fs::ResultDbmInvalidPathFormat()); + while (IsSeparator(path[1])) { + path++; + } + + this->prev_path_start = path; + this->prev_path_end = path; + this->next_path = path + 1; + while (IsSeparator(this->next_path[0])) { + this->next_path++; + } + + return ResultSuccess(); + } + + void PathParser::Finalize() { + this->prev_path_start = nullptr; + this->prev_path_end = nullptr; + this->next_path = nullptr; + this->finished = false; + } + + bool PathParser::IsFinished() const { + return this->finished; + } + + bool PathParser::IsDirectoryPath() const { + AMS_ASSERT(this->next_path != nullptr); + if (IsNullTerminator(this->next_path[0]) && IsSeparator(this->next_path[-1])) { + return true; + } + + if (IsCurrentDirectory(this->next_path)) { + return true; + } + + if (IsParentDirectory(this->next_path)) { + return true; + } + + return false; + } + + Result PathParser::GetAsDirectoryName(RomEntryName *out) const { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(this->prev_path_start != nullptr); + AMS_ASSERT(this->prev_path_end != nullptr); + AMS_ASSERT(this->next_path != nullptr); + + const size_t len = this->prev_path_end - this->prev_path_start; + R_UNLESS(len <= MaxPathLength, fs::ResultDbmDirectoryNameTooLong()); + + out->length = len; + out->path = this->prev_path_start; + return ResultSuccess(); + } + + Result PathParser::GetAsFileName(RomEntryName *out) const { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(this->prev_path_start != nullptr); + AMS_ASSERT(this->prev_path_end != nullptr); + AMS_ASSERT(this->next_path != nullptr); + + const size_t len = this->prev_path_end - this->prev_path_start; + R_UNLESS(len <= MaxPathLength, fs::ResultDbmFileNameTooLong()); + + out->length = len; + out->path = this->prev_path_start; + return ResultSuccess(); + } + + Result PathParser::GetNextDirectoryName(RomEntryName *out) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(this->prev_path_start != nullptr); + AMS_ASSERT(this->prev_path_end != nullptr); + AMS_ASSERT(this->next_path != nullptr); + + /* Set the current path to output. */ + out->length = this->prev_path_end - this->prev_path_start; + out->path = this->prev_path_start; + + /* Parse the next path. */ + this->prev_path_start = this->next_path; + const RomPathChar *cur = this->next_path; + for (size_t name_len = 0; true; name_len++) { + if (IsSeparator(cur[name_len])) { + R_UNLESS(name_len < MaxPathLength, fs::ResultDbmDirectoryNameTooLong()); + + this->prev_path_end = cur + name_len; + this->next_path = this->prev_path_end + 1; + + while (IsSeparator(this->next_path[0])) { + this->next_path++; + } + if (IsNullTerminator(this->next_path[0])) { + this->finished = true; + } + break; + } + + if (IsNullTerminator(cur[name_len])) { + this->finished = true; + this->prev_path_end = this->next_path = cur + name_len; + break; + } + } + + return ResultSuccess(); + } + + Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(p != nullptr); + + const RomPathChar *start = cur.path; + const RomPathChar *end = cur.path + cur.length - 1; + + s32 depth = 1; + if (IsParentDirectory(cur)) { + depth++; + } + + if (cur.path > p) { + size_t len = 0; + const RomPathChar *head = cur.path - 1; + while (head >= p) { + if (IsSeparator(*head)) { + if (IsCurrentDirectory(head + 1, len)) { + depth++; + } + + if (IsParentDirectory(head + 1, len)) { + depth += 2; + } + + if (depth == 0) { + start = head + 1; + break; + } + + while (IsSeparator(*head)) { + head--; + } + + end = head; + len = 0; + depth--; + } + + len++; + head--; + } + + R_UNLESS(depth == 0, fs::ResultDirectoryUnobtainable()); + + if (head == p) { + start = p + 1; + } + } + + if (end <= p) { + out->path = p; + out->length = 0; + } else { + out->path = start; + out->length = end - start + 1; + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_file_path_hash.hpp b/libraries/libstratosphere/source/fs/fs_file_path_hash.hpp new file mode 100644 index 000000000..bbc580839 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_file_path_hash.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + constexpr inline size_t FilePathHashSize = 4; + + struct FilePathHash : public Newable { + u8 data[FilePathHashSize]; + }; + static_assert(std::is_pod::value); + + inline bool operator==(const FilePathHash &lhs, const FilePathHash &rhs) { + return std::memcmp(lhs.data, rhs.data, FilePathHashSize) == 0; + } + + inline bool operator!=(const FilePathHash &lhs, const FilePathHash &rhs) { + return !(lhs == rhs); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_file_storage.cpp b/libraries/libstratosphere/source/fs/fs_file_storage.cpp index f6f510286..88105baab 100644 --- a/libraries/libstratosphere/source/fs/fs_file_storage.cpp +++ b/libraries/libstratosphere/source/fs/fs_file_storage.cpp @@ -18,13 +18,13 @@ namespace ams::fs { Result FileStorage::UpdateSize() { - R_UNLESS(this->size == InvalidSize, ResultSuccess()); + R_SUCCEED_IF(this->size != InvalidSize); return this->base_file->GetSize(&this->size); } Result FileStorage::Read(s64 offset, void *buffer, size_t size) { /* Immediately succeed if there's nothing to read. */ - R_UNLESS(size > 0, ResultSuccess()); + R_SUCCEED_IF(size == 0); /* Validate buffer. */ R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); @@ -41,7 +41,7 @@ namespace ams::fs { Result FileStorage::Write(s64 offset, const void *buffer, size_t size) { /* Immediately succeed if there's nothing to write. */ - R_UNLESS(size > 0, ResultSuccess()); + R_SUCCEED_IF(size == 0); /* Validate buffer. */ R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); @@ -86,7 +86,7 @@ namespace ams::fs { R_UNLESS(IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); default: - return fs::ResultUnsupportedOperation(); + return fs::ResultUnsupportedOperationInFileStorageA(); } } diff --git a/libraries/libstratosphere/source/fs/fs_game_card.cpp b/libraries/libstratosphere/source/fs/fs_game_card.cpp new file mode 100644 index 000000000..d2ac95235 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_game_card.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + namespace { + + const char *GetGameCardMountNameSuffix(GameCardPartition which) { + switch (which) { + case GameCardPartition::Update: return impl::GameCardFileSystemMountNameUpdateSuffix; + case GameCardPartition::Normal: return impl::GameCardFileSystemMountNameNormalSuffix; + case GameCardPartition::Secure: return impl::GameCardFileSystemMountNameSecureSuffix; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + class GameCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable { + private: + const GameCardHandle handle; + const GameCardPartition partition; + public: + explicit GameCardCommonMountNameGenerator(GameCardHandle h, GameCardPartition p) : handle(h), partition(p) { /* ... */ } + + virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override { + /* Determine how much space we need. */ + const size_t needed_size = strnlen(impl::GameCardFileSystemMountName, MountNameLengthMax) + strnlen(GetGameCardMountNameSuffix(this->partition), MountNameLengthMax) + sizeof(GameCardHandle) * 2 + 2; + AMS_ABORT_UNLESS(dst_size >= needed_size); + + /* Generate the name. */ + auto size = std::snprintf(dst, dst_size, "%s%s%08x:", impl::GameCardFileSystemMountName, GetGameCardMountNameSuffix(this->partition), this->handle); + AMS_ASSERT(static_cast(size) == needed_size - 1); + + return ResultSuccess(); + } + }; + + } + + Result GetGameCardHandle(GameCardHandle *out) { + /* TODO: fs::DeviceOperator */ + /* Open a DeviceOperator. */ + ::FsDeviceOperator d; + R_TRY(fsOpenDeviceOperator(std::addressof(d))); + ON_SCOPE_EXIT { fsDeviceOperatorClose(std::addressof(d)); }; + + /* Get the handle. */ + static_assert(sizeof(GameCardHandle) == sizeof(::FsGameCardHandle)); + return fsDeviceOperatorGetGameCardHandle(std::addressof(d), reinterpret_cast<::FsGameCardHandle *>(out)); + } + + Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountNameAllowingReserved(name)); + + /* Open gamecard filesystem. This uses libnx bindings. */ + ::FsFileSystem fs; + const ::FsGameCardHandle _hnd = {handle}; + R_TRY(fsOpenGameCardFileSystem(std::addressof(fs), std::addressof(_hnd), static_cast<::FsGameCardPartition>(partition))); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInGameCardC()); + + /* Allocate a new mountname generator. */ + auto generator = std::make_unique(handle, partition); + R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInGameCardD()); + + /* Register. */ + return fsa::Register(name, std::move(fsa), std::move(generator)); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_memory_management.cpp b/libraries/libstratosphere/source/fs/fs_memory_management.cpp new file mode 100644 index 000000000..ccf3f4396 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_memory_management.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::fs { + + namespace { + + bool g_used_default_allocator; + + void *DefaultAllocate(size_t size) { + g_used_default_allocator = true; + return std::malloc(size); + } + + void DefaultDeallocate(void *ptr, size_t size) { + std::free(ptr); + } + + os::Mutex g_lock; + AllocateFunction g_allocate_func = DefaultAllocate; + DeallocateFunction g_deallocate_func = DefaultDeallocate; + + constexpr size_t RequiredAlignment = alignof(u64); + + Result SetAllocatorImpl(AllocateFunction allocator, DeallocateFunction deallocator) { + /* Ensure SetAllocator is used correctly. */ + R_UNLESS(g_allocate_func == DefaultAllocate, fs::ResultAllocatorAlreadyRegistered()); + R_UNLESS(g_deallocate_func == DefaultDeallocate, fs::ResultAllocatorAlreadyRegistered()); + R_UNLESS(allocator != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(deallocator != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(!g_used_default_allocator, fs::ResultDefaultAllocatorUsed()); + + /* Set allocators. */ + g_allocate_func = allocator; + g_deallocate_func = deallocator; + return ResultSuccess(); + } + + } + + void SetAllocator(AllocateFunction allocator, DeallocateFunction deallocator) { + R_ABORT_UNLESS(SetAllocatorImpl(allocator, deallocator)); + } + + namespace impl { + + void *Allocate(size_t size) { + void *ptr; + { + std::scoped_lock lk(g_lock); + ptr = g_allocate_func(size); + if (!util::IsAligned(reinterpret_cast(ptr), RequiredAlignment)) { + R_ABORT_UNLESS(fs::ResultAllocatorAlignmentViolation()); + } + } + return ptr; + } + + void Deallocate(void *ptr, size_t size) { + if (ptr == nullptr) { + return; + } + std::scoped_lock lk(g_lock); + g_deallocate_func(ptr, size); + } + + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/fs/fs_path_utils.cpp b/libraries/libstratosphere/source/fs/fs_path_utils.cpp index 5b60e2732..db15d5b8f 100644 --- a/libraries/libstratosphere/source/fs/fs_path_utils.cpp +++ b/libraries/libstratosphere/source/fs/fs_path_utils.cpp @@ -25,7 +25,7 @@ namespace ams::fs { const char c = *(cur++); /* If terminated, we're done. */ - R_UNLESS(c != StringTraits::NullTerminator, ResultSuccess()); + R_SUCCEED_IF(PathTool::IsNullTerminator(c)); /* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */ /* We should do this. */ diff --git a/libraries/libstratosphere/source/fs/fs_rights_id.cpp b/libraries/libstratosphere/source/fs/fs_rights_id.cpp new file mode 100644 index 000000000..a0f6d6cd8 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_rights_id.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include + +namespace ams::fs { + + Result GetRightsId(RightsId *out, const char *path) { + static_assert(sizeof(RightsId) == sizeof(::FsRightsId)); + return fsGetRightsIdByPath(path, reinterpret_cast<::FsRightsId *>(out)); + } + + Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path) { + static_assert(sizeof(RightsId) == sizeof(::FsRightsId)); + return fsGetRightsIdAndKeyGenerationByPath(path, out_key_generation, reinterpret_cast<::FsRightsId *>(out)); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/fs/fs_romfs_filesystem.cpp b/libraries/libstratosphere/source/fs/fs_romfs_filesystem.cpp new file mode 100644 index 000000000..95b24835a --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_romfs_filesystem.cpp @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::fs { + + namespace { + + Result ConvertNcaCorruptedResult(Result res) { + AMS_ASSERT(fs::ResultNcaCorrupted::Includes(res)); + + R_TRY_CATCH(res) { + R_CONVERT(fs::ResultInvalidNcaFileSystemType, fs::ResultInvalidRomNcaFileSystemType()) + R_CONVERT(fs::ResultInvalidAcidFileSize, fs::ResultInvalidRomAcidFileSize()) + R_CONVERT(fs::ResultInvalidAcidSize, fs::ResultInvalidRomAcidSize()) + R_CONVERT(fs::ResultInvalidAcid, fs::ResultInvalidRomAcid()) + R_CONVERT(fs::ResultAcidVerificationFailed, fs::ResultRomAcidVerificationFailed()) + R_CONVERT(fs::ResultInvalidNcaSignature, fs::ResultInvalidRomNcaSignature()) + R_CONVERT(fs::ResultNcaHeaderSignature1VerificationFailed, fs::ResultRomNcaHeaderSignature1VerificationFailed()) + R_CONVERT(fs::ResultNcaHeaderSignature2VerificationFailed, fs::ResultRomNcaHeaderSignature2VerificationFailed()) + R_CONVERT(fs::ResultNcaFsHeaderHashVerificationFailed, fs::ResultRomNcaFsHeaderHashVerificationFailed()) + R_CONVERT(fs::ResultInvalidNcaKeyIndex, fs::ResultInvalidRomNcaKeyIndex()) + R_CONVERT(fs::ResultInvalidNcaFsHeaderHashType, fs::ResultInvalidRomNcaFsHeaderHashType()) + R_CONVERT(fs::ResultInvalidNcaFsHeaderEncryptionType, fs::ResultInvalidRomNcaFsHeaderEncryptionType()) + R_CONVERT(fs::ResultInvalidHierarchicalSha256BlockSize, fs::ResultInvalidRomHierarchicalSha256BlockSize()) + R_CONVERT(fs::ResultInvalidHierarchicalSha256LayerCount, fs::ResultInvalidRomHierarchicalSha256LayerCount()) + R_CONVERT(fs::ResultHierarchicalSha256BaseStorageTooLarge, fs::ResultRomHierarchicalSha256BaseStorageTooLarge()) + R_CONVERT(fs::ResultHierarchicalSha256HashVerificationFailed, fs::ResultRomHierarchicalSha256HashVerificationFailed()) + R_CATCH_ALL() { /* ... */ } + } R_END_TRY_CATCH; + + AMS_ASSERT(false); + return fs::ResultNcaCorrupted(); + } + + Result ConvertIntegrityVerificationStorageCorruptedResult(Result res) { + AMS_ASSERT(fs::ResultIntegrityVerificationStorageCorrupted::Includes(res)); + + R_TRY_CATCH(res) { + R_CONVERT(fs::ResultIncorrectIntegrityVerificationMagic, fs::ResultIncorrectRomIntegrityVerificationMagic()) + R_CONVERT(fs::ResultInvalidZeroHash, fs::ResultInvalidRomZeroHash()) + R_CONVERT(fs::ResultNonRealDataVerificationFailed, fs::ResultRomNonRealDataVerificationFailed()) + R_CONVERT(fs::ResultInvalidHierarchicalIntegrityVerificationLayerCount, fs::ResultInvalidRomHierarchicalIntegrityVerificationLayerCount()) + R_CONVERT(fs::ResultClearedRealDataVerificationFailed, fs::ResultClearedRomRealDataVerificationFailed()) + R_CONVERT(fs::ResultUnclearedRealDataVerificationFailed, fs::ResultUnclearedRomRealDataVerificationFailed()) + R_CATCH_ALL() { /* ... */ } + } R_END_TRY_CATCH; + + AMS_ASSERT(false); + return fs::ResultIntegrityVerificationStorageCorrupted(); + } + + Result ConvertBuiltInStorageCorruptedResult(Result res) { + AMS_ASSERT(fs::ResultBuiltInStorageCorrupted::Includes(res)); + + R_TRY_CATCH(res) { + R_CONVERT(fs::ResultGptHeaderVerificationFailed, fs::ResultRomGptHeaderVerificationFailed()) + R_CATCH_ALL() { /* ... */ } + } R_END_TRY_CATCH; + + AMS_ASSERT(false); + return fs::ResultBuiltInStorageCorrupted(); + } + + Result ConvertPartitionFileSystemCorruptedResult(Result res) { + AMS_ASSERT(fs::ResultPartitionFileSystemCorrupted::Includes(res)); + + R_TRY_CATCH(res) { + R_CONVERT(fs::ResultInvalidSha256PartitionHashTarget, fs::ResultInvalidRomSha256PartitionHashTarget()) + R_CONVERT(fs::ResultSha256PartitionHashVerificationFailed, fs::ResultRomSha256PartitionHashVerificationFailed()) + R_CONVERT(fs::ResultPartitionSignatureVerificationFailed, fs::ResultRomPartitionSignatureVerificationFailed()) + R_CONVERT(fs::ResultSha256PartitionSignatureVerificationFailed, fs::ResultRomSha256PartitionSignatureVerificationFailed()) + R_CONVERT(fs::ResultInvalidPartitionEntryOffset, fs::ResultInvalidRomPartitionEntryOffset()) + R_CONVERT(fs::ResultInvalidSha256PartitionMetaDataSize, fs::ResultInvalidRomSha256PartitionMetaDataSize()) + R_CATCH_ALL() { /* ... */ } + } R_END_TRY_CATCH; + + AMS_ASSERT(false); + return fs::ResultPartitionFileSystemCorrupted(); + } + + Result ConvertFatFileSystemCorruptedResult(Result res) { + AMS_ASSERT(fs::ResultFatFileSystemCorrupted::Includes(res)); + + return res; + } + + Result ConvertHostFileSystemCorruptedResult(Result res) { + AMS_ASSERT(fs::ResultHostFileSystemCorrupted::Includes(res)); + + R_TRY_CATCH(res) { + R_CONVERT(fs::ResultHostEntryCorrupted, fs::ResultRomHostEntryCorrupted()) + R_CONVERT(fs::ResultHostFileDataCorrupted, fs::ResultRomHostFileDataCorrupted()) + R_CONVERT(fs::ResultHostFileCorrupted, fs::ResultRomHostFileCorrupted()) + R_CONVERT(fs::ResultInvalidHostHandle, fs::ResultInvalidRomHostHandle()) + R_CATCH_ALL() { /* ... */ } + } R_END_TRY_CATCH; + + AMS_ASSERT(false); + return fs::ResultHostFileSystemCorrupted(); + } + + Result ConvertDatabaseCorruptedResult(Result res) { + AMS_ASSERT(fs::ResultDatabaseCorrupted::Includes(res)); + + R_TRY_CATCH(res) { + R_CONVERT(fs::ResultInvalidAllocationTableBlock, fs::ResultInvalidRomAllocationTableBlock()) + R_CONVERT(fs::ResultInvalidKeyValueListElementIndex, fs::ResultInvalidRomKeyValueListElementIndex()) + R_CATCH_ALL() { /* ... */ } + } R_END_TRY_CATCH; + + AMS_ASSERT(false); + return fs::ResultDatabaseCorrupted(); + } + + Result ConvertRomFsResult(Result res) { + R_TRY_CATCH(res) { + R_CONVERT(fs::ResultUnsupportedVersion, fs::ResultUnsupportedRomVersion()) + R_CONVERT(fs::ResultNcaCorrupted, ConvertNcaCorruptedResult(res)) + R_CONVERT(fs::ResultIntegrityVerificationStorageCorrupted, ConvertIntegrityVerificationStorageCorruptedResult(res)) + R_CONVERT(fs::ResultBuiltInStorageCorrupted, ConvertBuiltInStorageCorruptedResult(res)) + R_CONVERT(fs::ResultPartitionFileSystemCorrupted, ConvertPartitionFileSystemCorruptedResult(res)) + R_CONVERT(fs::ResultFatFileSystemCorrupted, ConvertFatFileSystemCorruptedResult(res)) + R_CONVERT(fs::ResultHostFileSystemCorrupted, ConvertHostFileSystemCorruptedResult(res)) + R_CONVERT(fs::ResultDatabaseCorrupted, ConvertDatabaseCorruptedResult(res)) + R_CONVERT(fs::ResultNotFound, fs::ResultPathNotFound()) + R_CONVERT(fs::ResultPermissionDenied, fs::ResultTargetLocked()) + R_CONVERT(fs::ResultIncompatiblePath, fs::ResultPathNotFound()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result ReadFile(IStorage *storage, s64 offset, void *buffer, size_t size) { + AMS_ASSERT(storage != nullptr); + AMS_ASSERT(offset >= 0); + AMS_ASSERT(buffer != nullptr || size == 0); + + return ConvertRomFsResult(storage->Read(offset, buffer, size)); + } + + Result ReadFileHeader(IStorage *storage, RomFileSystemInformation *out) { + AMS_ASSERT(storage != nullptr); + AMS_ASSERT(out != nullptr); + + return ReadFile(storage, 0, out, sizeof(*out)); + } + + constexpr size_t CalculateRequiredWorkingMemorySize(const RomFileSystemInformation &header) { + const size_t needed_size = header.directory_bucket_size + header.directory_entry_size + header.file_bucket_size + header.file_entry_size; + return util::AlignUp(needed_size, 8); + } + + class RomFsFile : public fsa::IFile, public impl::Newable { + private: + RomFsFileSystem *parent; + s64 start; + s64 end; + public: + RomFsFile(RomFsFileSystem *p, s64 s, s64 e) : parent(p), start(s), end(e) { /* ... */ } + virtual ~RomFsFile() { /* ... */ } + + Result VerifyArguments(size_t *out, s64 offset, void *buf, size_t size, const fs::ReadOption &option) { + R_TRY(DryRead(out, offset, size, option, fs::OpenMode_Read)); + + AMS_ASSERT(this->GetStorage() != nullptr); + AMS_ASSERT(offset >= 0); + AMS_ASSERT(buf != nullptr || size == 0); + + return ResultSuccess(); + } + + Result ConvertResult(Result res) const { + return ConvertRomFsResult(res); + } + + s64 GetOffset() const { + return this->start; + } + + s64 GetSize() const { + return this->end - this->start; + } + + IStorage *GetStorage() { + return this->parent->GetBaseStorage(); + } + public: + virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override { + size_t read_size = 0; + R_TRY(this->VerifyArguments(std::addressof(read_size), offset, buffer, size, option)); + + R_TRY(this->ConvertResult(this->GetStorage()->Read(offset + this->start, buffer, size))); + *out = read_size; + + return ResultSuccess(); + } + + virtual Result GetSizeImpl(s64 *out) override { + *out = this->GetSize(); + return ResultSuccess(); + } + + virtual Result FlushImpl() override { + return ResultSuccess(); + } + + virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override { + return fs::ResultUnsupportedOperationInRomFsFileA(); + } + + virtual Result SetSizeImpl(s64 size) override { + return fs::ResultUnsupportedOperationInRomFsFileA(); + } + + virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + switch (op_id) { + case OperationId::InvalidateCache: + case OperationId::QueryRange: + { + R_UNLESS(offset >= 0, fs::ResultOutOfRange()); + R_UNLESS(this->GetSize() >= 0, fs::ResultOutOfRange()); + } + default: + return fs::ResultUnsupportedOperationInRomFsFileB(); + } + } + public: + virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { + AMS_ABORT(); + } + }; + + class RomFsDirectory : public fsa::IDirectory, public impl::Newable { + private: + using FindPosition = RomFsFileSystem::RomFileTable::FindPosition; + private: + RomFsFileSystem *parent; + FindPosition current_find; + FindPosition first_find; + fs::OpenDirectoryMode mode; + public: + RomFsDirectory(RomFsFileSystem *p, const FindPosition &f, fs::OpenDirectoryMode m) : parent(p), current_find(f), first_find(f), mode(m) { /* ... */ } + virtual ~RomFsDirectory() override { /* ... */ } + public: + virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) { + return this->ReadImpl(out_count, std::addressof(this->current_find), out_entries, max_entries); + } + + virtual Result GetEntryCountImpl(s64 *out) { + FindPosition find = this->first_find; + return this->ReadImpl(out, std::addressof(find), nullptr, 0); + } + private: + Result ReadImpl(s64 *out_count, FindPosition *find, DirectoryEntry *out_entries, s64 max_entries) { + AMS_ASSERT(out_count != nullptr); + AMS_ASSERT(find != nullptr); + + constexpr size_t NameBufferSize = fs::EntryNameLengthMax + 1; + char *name_buf = static_cast(::ams::fs::impl::Allocate(NameBufferSize)); + R_UNLESS(name_buf != nullptr, fs::ResultAllocationFailureInRomFsFileSystemE()); + ON_SCOPE_EXIT { ::ams::fs::impl::Deallocate(name_buf, NameBufferSize); }; + + s32 i = 0; + + if (this->mode & fs::OpenDirectoryMode_Directory) { + while (i < max_entries || out_entries == nullptr) { + R_TRY_CATCH(this->parent->GetRomFileTable()->FindNextDirectory(name_buf, find, NameBufferSize)) { + R_CATCH(fs::ResultDbmFindFinished) { break; } + } R_END_TRY_CATCH; + + if (out_entries) { + R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath()); + strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax); + out_entries[i].name[fs::EntryNameLengthMax] = '\x00'; + out_entries[i].type = fs::DirectoryEntryType_Directory; + out_entries[i].file_size = 0; + } + + i++; + } + } + + if (this->mode & fs::OpenDirectoryMode_File) { + while (i < max_entries || out_entries == nullptr) { + auto file_pos = find->next_file; + + R_TRY_CATCH(this->parent->GetRomFileTable()->FindNextFile(name_buf, find, NameBufferSize)) { + R_CATCH(fs::ResultDbmFindFinished) { break; } + } R_END_TRY_CATCH; + + if (out_entries) { + R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath()); + strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax); + out_entries[i].name[fs::EntryNameLengthMax] = '\x00'; + out_entries[i].type = fs::DirectoryEntryType_File; + + RomFsFileSystem::RomFileTable::FileInfo file_info; + R_TRY(this->parent->GetRomFileTable()->OpenFile(std::addressof(file_info), this->parent->GetRomFileTable()->ConvertToFileId(file_pos))); + out_entries[i].file_size = file_info.size.Get(); + } + + i++; + } + } + + *out_count = i; + return ResultSuccess(); + } + public: + virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { + AMS_ABORT(); + } + }; + + } + + + RomFsFileSystem::RomFsFileSystem() : base_storage() { + /* ... */ + } + + RomFsFileSystem::~RomFsFileSystem() { + /* ... */ + } + + Result RomFsFileSystem::GetRequiredWorkingMemorySize(size_t *out, IStorage *storage) { + RomFileSystemInformation header; + R_TRY(ReadFileHeader(storage, std::addressof(header))); + + *out = CalculateRequiredWorkingMemorySize(header); + return ResultSuccess(); + } + + Result RomFsFileSystem::Initialize(IStorage *base, void *work, size_t work_size, bool use_cache) { + AMS_ABORT_UNLESS(!use_cache || work != nullptr); + AMS_ABORT_UNLESS(base != nullptr); + + /* Read the header. */ + RomFileSystemInformation header; + R_TRY(ReadFileHeader(base, std::addressof(header))); + + /* Set up our storages. */ + if (use_cache) { + const size_t needed_size = CalculateRequiredWorkingMemorySize(header); + R_UNLESS(work_size >= needed_size, fs::ResultPreconditionViolation()); + + u8 *buf = static_cast(work); + auto dir_bucket_buf = buf; buf += header.directory_bucket_size; + auto dir_entry_buf = buf; buf += header.directory_entry_size; + auto file_bucket_buf = buf; buf += header.file_bucket_size; + auto file_entry_buf = buf; buf += header.file_entry_size; + + R_TRY(ReadFile(base, header.directory_bucket_offset, dir_bucket_buf, header.directory_bucket_size)); + R_TRY(ReadFile(base, header.directory_entry_offset, dir_entry_buf, header.directory_entry_size)); + R_TRY(ReadFile(base, header.file_bucket_offset, file_bucket_buf, header.file_bucket_size)); + R_TRY(ReadFile(base, header.file_entry_offset, file_entry_buf, header.file_entry_size)); + + this->dir_bucket_storage.reset(new MemoryStorage(dir_bucket_buf, header.directory_bucket_size)); + this->dir_entry_storage.reset(new MemoryStorage(dir_entry_buf, header.directory_entry_size)); + this->file_bucket_storage.reset(new MemoryStorage(file_bucket_buf, header.file_bucket_size)); + this->file_entry_storage.reset(new MemoryStorage(file_entry_buf, header.file_entry_size)); + } else { + this->dir_bucket_storage.reset(new SubStorage(base, header.directory_bucket_offset, header.directory_bucket_size)); + this->dir_entry_storage.reset(new SubStorage(base, header.directory_entry_offset, header.directory_entry_size)); + this->file_bucket_storage.reset(new SubStorage(base, header.file_bucket_offset, header.file_bucket_size)); + this->file_entry_storage.reset(new SubStorage(base, header.file_entry_offset, header.file_entry_size)); + } + + /* Ensure we allocated storages successfully. */ + R_UNLESS(this->dir_bucket_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA()); + R_UNLESS(this->dir_entry_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA()); + R_UNLESS(this->file_bucket_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA()); + R_UNLESS(this->file_entry_storage != nullptr, fs::ResultAllocationFailureInRomFsFileSystemA()); + + /* Initialize the rom table. */ + { + + SubStorage db(this->dir_bucket_storage.get(), 0, header.directory_bucket_size); + SubStorage de(this->dir_entry_storage.get(), 0, header.directory_entry_size); + SubStorage fb(this->file_bucket_storage.get(), 0, header.file_bucket_size); + SubStorage fe(this->file_entry_storage.get(), 0, header.file_entry_size); + R_TRY(this->rom_file_table.Initialize(db, de, fb, fe)); + } + + /* Set members. */ + this->entry_size = header.body_offset; + this->base_storage = base; + return ResultSuccess(); + } + + Result RomFsFileSystem::Initialize(std::unique_ptr&& base, void *work, size_t work_size, bool use_cache) { + this->unique_storage = std::move(base); + return this->Initialize(this->unique_storage.get(), work, work_size, use_cache); + } + + Result RomFsFileSystem::GetFileInfo(RomFileTable::FileInfo *out, const char *path) { + R_TRY_CATCH(this->rom_file_table.OpenFile(out, path)) { + R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound()); + R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultPathNotFound()); + } R_END_TRY_CATCH; + return ResultSuccess(); + } + + IStorage *RomFsFileSystem::GetBaseStorage() { + return this->base_storage; + } + + RomFsFileSystem::RomFileTable *RomFsFileSystem::GetRomFileTable() { + return std::addressof(this->rom_file_table); + } + + Result RomFsFileSystem::GetFileBaseOffset(s64 *out, const char *path) { + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(path != nullptr); + + RomFileTable::FileInfo info; + R_TRY(this->GetFileInfo(std::addressof(info), path)); + *out = this->entry_size + info.offset.Get(); + return ResultSuccess(); + } + + Result RomFsFileSystem::CreateFileImpl(const char *path, s64 size, int flags) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::DeleteFileImpl(const char *path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::CreateDirectoryImpl(const char *path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::DeleteDirectoryImpl(const char *path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::DeleteDirectoryRecursivelyImpl(const char *path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::RenameFileImpl(const char *old_path, const char *new_path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::RenameDirectoryImpl(const char *old_path, const char *new_path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) { + RomDirectoryInfo dir_info; + R_TRY_CATCH(this->rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) { + R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound()) + R_CATCH(fs::ResultDbmInvalidOperation) { + RomFileTable::FileInfo file_info; + R_TRY(this->GetFileInfo(std::addressof(file_info), path)); + *out = fs::DirectoryEntryType_File; + return ResultSuccess(); + } + } R_END_TRY_CATCH; + + *out = fs::DirectoryEntryType_Directory; + return ResultSuccess(); + } + + Result RomFsFileSystem::OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) { + AMS_ASSERT(out_file != nullptr); + AMS_ASSERT(path != nullptr); + + R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument()); + + RomFileTable::FileInfo file_info; + R_TRY(this->GetFileInfo(std::addressof(file_info), path)); + + auto file = std::make_unique(this, this->entry_size + file_info.offset.Get(), this->entry_size + file_info.offset.Get() + file_info.size.Get()); + R_UNLESS(file != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB()); + + *out_file = std::move(file); + return ResultSuccess(); + } + + Result RomFsFileSystem::OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, fs::OpenDirectoryMode mode) { + AMS_ASSERT(out_dir != nullptr); + AMS_ASSERT(path != nullptr); + + RomFileTable::FindPosition find; + R_TRY_CATCH(this->rom_file_table.FindOpen(std::addressof(find), path)) { + R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound()) + R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultPathNotFound()) + } R_END_TRY_CATCH; + + auto dir = std::make_unique(this, find, mode); + R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInRomFsFileSystemC()); + + *out_dir = std::move(dir); + return ResultSuccess(); + } + + Result RomFsFileSystem::CommitImpl() { + return ResultSuccess(); + } + + Result RomFsFileSystem::GetFreeSpaceSizeImpl(s64 *out, const char *path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemC(); + } + + Result RomFsFileSystem::GetTotalSpaceSizeImpl(s64 *out, const char *path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemC(); + } + + Result RomFsFileSystem::CleanDirectoryRecursivelyImpl(const char *path) { + return fs::ResultUnsupportedOperationInRomFsFileSystemA(); + } + + Result RomFsFileSystem::CommitProvisionallyImpl(s64 counter) { + return fs::ResultUnsupportedOperationInRomFsFileSystemB(); + } + + Result RomFsFileSystem::RollbackImpl() { + return ResultSuccess(); + } +} diff --git a/libraries/libstratosphere/source/fs/fs_save_data_management.cpp b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp new file mode 100644 index 000000000..6185029fe --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + namespace impl { + + Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataId id) { + return fsReadSaveDataFileSystemExtraData(out, sizeof(*out), id); + } + + Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataSpaceId space_id, SaveDataId id) { + return fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(out, sizeof(*out), static_cast<::FsSaveDataSpaceId>(space_id), id); + } + + Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId space_id, SaveDataId id, const SaveDataExtraData &extra_data) { + return fsWriteSaveDataFileSystemExtraData(std::addressof(extra_data), sizeof(extra_data), static_cast<::FsSaveDataSpaceId>(space_id), id); + } + + } + + void DisableAutoSaveDataCreation() { + /* Use libnx binding. */ + R_ABORT_UNLESS(fsDisableAutoSaveDataCreation()); + } + + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, save_id); + const SaveDataCreationInfo info = { + .size = size, + .journal_size = journal_size, + .block_size = DefaultSaveDataBlockSize, + .owner_id = owner_id, + .flags = flags, + .space_id = space_id, + .pseudo = false, + }; + + static_assert(sizeof(SaveDataAttribute) == sizeof(::FsSaveDataAttribute)); + static_assert(sizeof(SaveDataCreationInfo) == sizeof(::FsSaveDataCreationInfo)); + return fsCreateSaveDataFileSystemBySystemSaveDataId(reinterpret_cast(std::addressof(attribute)), reinterpret_cast(std::addressof(info))); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, InvalidUserId, 0, size, journal_size, flags); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, InvalidUserId, owner_id, size, journal_size, flags); + } + + Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(space_id, save_id, InvalidUserId, owner_id, size, journal_size, flags); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, user_id, 0, size, journal_size, flags); + } + + Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) { + return CreateSystemSaveData(SaveDataSpaceId::System, save_id, user_id, owner_id, size, journal_size, flags); + } + + Result DeleteSaveData(SaveDataId id) { + /* TODO: Libnx binding for DeleteSaveDataFileSystem */ + AMS_ABORT(); + } + + Result DeleteSaveData(SaveDataSpaceId space_id, SaveDataId id) { + return fsDeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<::FsSaveDataSpaceId>(space_id), id); + } + + Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) { + const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id); + + /* TODO: Libnx binding for DeleteSaveDataFileSystemBySaveDataAttribute */ + AMS_UNUSED(attribute); + AMS_ABORT(); + } + + Result GetSaveDataFlags(u32 *out, SaveDataId id) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), id)); + + *out = extra_data.flags; + return ResultSuccess(); + } + + Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), space_id, id)); + + *out = extra_data.flags; + return ResultSuccess(); + } + + Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), space_id, id)); + extra_data.flags = flags; + return impl::WriteSaveDataFileSystemExtraData(space_id, id, extra_data); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_scoped_setter.hpp b/libraries/libstratosphere/source/fs/fs_scoped_setter.hpp new file mode 100644 index 000000000..d72dcbba3 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_scoped_setter.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs { + + template + class ScopedSetter { + NON_COPYABLE(ScopedSetter); + private: + T *ptr; + T value; + public: + constexpr ALWAYS_INLINE ScopedSetter(T &p, T v) : ptr(std::addressof(p)), value(v) { /* ... */ } + ALWAYS_INLINE ~ScopedSetter() { + if (this->ptr) { + *this->ptr = this->value; + } + } + + ALWAYS_INLINE ScopedSetter(ScopedSetter &&rhs) { + this->ptr = rhs.ptr; + this->value = rhs.value; + rhs.Reset(); + } + + ALWAYS_INLINE ScopedSetter &operator=(ScopedSetter &&rhs) { + this->ptr = rhs.ptr; + this->value = rhs.value; + rhs.Reset(); + return *this; + } + + ALWAYS_INLINE void Set(T v) { this->value = v; } + private: + ALWAYS_INLINE void Reset() { + this->ptr = nullptr; + } + }; + + template + ALWAYS_INLINE ScopedSetter MakeScopedSetter(T &p, T v) { + return ScopedSetter(p, v); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_sd_card.cpp b/libraries/libstratosphere/source/fs/fs_sd_card.cpp new file mode 100644 index 000000000..04798db8a --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_sd_card.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + Result MountSdCard(const char *name) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Open the SD card. This uses libnx bindings. */ + FsFileSystem fs; + R_TRY(fsOpenSdCardFileSystem(std::addressof(fs))); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA()); + + /* Register. */ + return fsa::Register(name, std::move(fsa)); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_signed_system_partition.cpp b/libraries/libstratosphere/source/fs/fs_signed_system_partition.cpp new file mode 100644 index 000000000..c41bcf9ee --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_signed_system_partition.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_filesystem_accessor.hpp" +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + bool IsSignedSystemPartitionOnSdCardValid(const char *system_root_path) { + /* Get the accessor for the system filesystem. */ + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_ABORT_UNLESS(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), system_root_path)); + + char is_valid; + R_TRY_CATCH(accessor->QueryEntry(std::addressof(is_valid), 1, nullptr, 0, fsa::QueryId::IsSignedSystemPartitionOnSdCardValid, "/")) { + /* If querying isn't supported, then the partition isn't valid. */ + R_CATCH(fs::ResultUnsupportedOperation) { is_valid = false; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return is_valid; + } + + bool IsSignedSystemPartitionOnSdCardValidDeprecated() { + /* Ensure we only call with correct version. */ + auto version = hos::GetVersion(); + AMS_ABORT_UNLESS(hos::Version_400 <= version && version < hos::Version_800); + + /* Check that the partition is valid. */ + bool is_valid; + R_ABORT_UNLESS(fsIsSignedSystemPartitionOnSdCardValid(std::addressof(is_valid))); + return is_valid; + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_system_data.cpp b/libraries/libstratosphere/source/fs/fs_system_data.cpp new file mode 100644 index 000000000..07d20140b --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_system_data.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + Result QueryMountSystemDataCacheSize(size_t *out, ncm::SystemDataId data_id) { + return impl::QueryMountDataCacheSize(out, data_id, ncm::StorageId::BuiltInSystem); + } + + Result MountSystemData(const char *name, ncm::SystemDataId data_id) { + return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem); + } + + Result MountSystemData(const char *name, ncm::SystemDataId data_id, void *cache_buffer, size_t cache_size) { + return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem, cache_buffer, cache_size); + } + +} diff --git a/libraries/libstratosphere/source/fs/fs_system_save_data.cpp b/libraries/libstratosphere/source/fs/fs_system_save_data.cpp new file mode 100644 index 000000000..89e888db3 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_system_save_data.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + namespace { + + Result MountSystemSaveDataImpl(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id, SaveDataType type) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Create the attribute. */ + const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, type, user_id, id); + static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute)); + + /* Open the filesystem, use libnx bindings. */ + ::FsFileSystem fs; + R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast(std::addressof(attribute)))); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSystemSaveDataA()); + + /* Register. */ + return fsa::Register(name, std::move(fsa)); + } + + } + + Result MountSystemSaveData(const char *name, SystemSaveDataId id) { + return MountSystemSaveData(name, id, InvalidUserId); + } + + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id) { + return MountSystemSaveData(name, space_id, id, InvalidUserId); + } + + Result MountSystemSaveData(const char *name, SystemSaveDataId id, UserId user_id) { + return MountSystemSaveData(name, SaveDataSpaceId::System, id, user_id); + } + + Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) { + return MountSystemSaveDataImpl(name, space_id, id, user_id, SaveDataType::System); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.cpp b/libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.cpp new file mode 100644 index 000000000..1cca9fcba --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_directory_accessor.hpp" +#include "fs_filesystem_accessor.hpp" + +namespace ams::fs::impl { + + DirectoryAccessor::DirectoryAccessor(std::unique_ptr&& d, FileSystemAccessor &p) : impl(std::move(d)), parent(p) { + /* ... */ + } + + DirectoryAccessor::~DirectoryAccessor() { + this->impl.reset(); + this->parent.NotifyCloseDirectory(this); + } + + Result DirectoryAccessor::Read(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) { + return this->impl->Read(out_count, out_entries, max_entries); + } + + Result DirectoryAccessor::GetEntryCount(s64 *out) { + return this->impl->GetEntryCount(out); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.hpp b/libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.hpp new file mode 100644 index 000000000..77bc44af5 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_directory_accessor.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + class FileSystemAccessor; + + class DirectoryAccessor : public util::IntrusiveListBaseNode, public Newable { + NON_COPYABLE(DirectoryAccessor); + private: + std::unique_ptr impl; + FileSystemAccessor &parent; + public: + DirectoryAccessor(std::unique_ptr&& d, FileSystemAccessor &p); + ~DirectoryAccessor(); + + Result Read(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries); + Result GetEntryCount(s64 *out); + + FileSystemAccessor &GetParent() const { return this->parent; } + }; + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_file_accessor.cpp b/libraries/libstratosphere/source/fs/fsa/fs_file_accessor.cpp new file mode 100644 index 000000000..d620d2a53 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_file_accessor.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../fs_scoped_setter.hpp" +#include "../fs_file_path_hash.hpp" +#include "fs_file_accessor.hpp" +#include "fs_filesystem_accessor.hpp" + +namespace ams::fs::impl { + + FileAccessor::FileAccessor(std::unique_ptr&& f, FileSystemAccessor *p, OpenMode mode) + : impl(std::move(f)), parent(p), write_state(WriteState::None), write_result(ResultSuccess()), open_mode(mode) + { + /* ... */ + } + + FileAccessor::~FileAccessor() { + /* Ensure that all files are flushed. */ + if (R_FAILED(this->write_result)) { + AMS_ABORT_UNLESS(this->write_state != WriteState::NeedsFlush); + } + this->impl.reset(); + + if (this->parent != nullptr) { + this->parent->NotifyCloseFile(this); + } + } + + Result FileAccessor::ReadWithCacheAccessLog(size_t *out, s64 offset, void *buf, size_t size, const ReadOption &option, bool use_path_cache, bool use_data_cache) { + AMS_ABORT(); + } + + Result FileAccessor::ReadWithoutCacheAccessLog(size_t *out, s64 offset, void *buf, size_t size, const ReadOption &option) { + return this->impl->Read(out, offset, buf, size, option); + } + + Result FileAccessor::Read(size_t *out, s64 offset, void *buf, size_t size, const ReadOption &option) { + /* Fail after a write fails. */ + R_TRY(this->write_result); + + /* TODO: Logging. */ + /* TODO: Support cache. */ + const bool use_path_cache = this->parent != nullptr && this->file_path_hash != nullptr; + const bool use_data_cache = /* TODO */false && this->parent != nullptr && this->parent->IsFileDataCacheAttachable(); + + if (use_path_cache && use_data_cache && false) { + /* TODO */ + return this->ReadWithCacheAccessLog(out, offset, buf, size, option, use_path_cache, use_data_cache); + } else { + return this->ReadWithoutCacheAccessLog(out, offset, buf, size, option); + } + } + + Result FileAccessor::Write(s64 offset, const void *buf, size_t size, const WriteOption &option) { + /* Fail after a write fails. */ + R_TRY(this->write_result); + + auto setter = MakeScopedSetter(this->write_state, WriteState::Failed); + if (this->file_path_hash != nullptr && /* TODO */ false) { + /* TODO */ + AMS_ABORT(); + } else { + R_TRY(this->UpdateLastResult(this->impl->Write(offset, buf, size, option))); + } + + setter.Set(option.HasFlushFlag() ? WriteState::None : WriteState::NeedsFlush); + + return ResultSuccess(); + } + + Result FileAccessor::Flush() { + /* Fail after a write fails. */ + R_TRY(this->write_result); + + auto setter = MakeScopedSetter(this->write_state, WriteState::Failed); + R_TRY(this->UpdateLastResult(this->impl->Flush())); + setter.Set(WriteState::None); + + return ResultSuccess(); + } + + Result FileAccessor::SetSize(s64 size) { + /* Fail after a write fails. */ + R_TRY(this->write_result); + + const WriteState old_write_state = this->write_state; + auto setter = MakeScopedSetter(this->write_state, WriteState::Failed); + + R_TRY(this->UpdateLastResult(this->impl->SetSize(size))); + + if (this->file_path_hash != nullptr) { + /* TODO: invalidate path cache */ + } + + setter.Set(old_write_state); + return ResultSuccess(); + } + + Result FileAccessor::GetSize(s64 *out) { + /* Fail after a write fails. */ + R_TRY(this->write_result); + + return this->impl->GetSize(out); + } + + Result FileAccessor::OperateRange(void *dst, size_t dst_size, OperationId operation, s64 offset, s64 size, const void *src, size_t src_size) { + return this->impl->OperateRange(dst, dst_size, operation, offset, size, src, src_size); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/fs/fsa/fs_file_accessor.hpp b/libraries/libstratosphere/source/fs/fsa/fs_file_accessor.hpp new file mode 100644 index 000000000..ddb2ac2ab --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_file_accessor.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + struct FilePathHash; + class FileSystemAccessor; + + enum class WriteState { + None, + NeedsFlush, + Failed, + }; + + class FileAccessor : public util::IntrusiveListBaseNode, public Newable { + NON_COPYABLE(FileAccessor); + private: + std::unique_ptr impl; + FileSystemAccessor * const parent; + WriteState write_state; + Result write_result; + const OpenMode open_mode; + std::unique_ptr file_path_hash; + s32 path_hash_index; + public: + FileAccessor(std::unique_ptr&& f, FileSystemAccessor *p, OpenMode mode); + ~FileAccessor(); + + Result Read(size_t *out, s64 offset, void *buf, size_t size, const ReadOption &option); + Result Write(s64 offset, const void *buf, size_t size, const WriteOption &option); + Result Flush(); + Result SetSize(s64 size); + Result GetSize(s64 *out); + Result OperateRange(void *dst, size_t dst_size, OperationId operation, s64 offset, s64 size, const void *src, size_t src_size); + + OpenMode GetOpenMode() const { return this->open_mode; } + WriteState GetWriteState() const { return this->write_state; } + FileSystemAccessor *GetParent() const { return this->parent; } + + void SetFilePathHash(std::unique_ptr&& file_path_hash, s32 index); + Result ReadWithoutCacheAccessLog(size_t *out, s64 offset, void *buf, size_t size, const ReadOption &option); + private: + Result ReadWithCacheAccessLog(size_t *out, s64 offset, void *buf, size_t size, const ReadOption &option, bool use_path_cache, bool use_data_cache); + + ALWAYS_INLINE Result UpdateLastResult(Result r) { + if (!fs::ResultNotEnoughFreeSpace::Includes(r)) { + this->write_result = r; + } + return r; + } + }; + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp new file mode 100644 index 000000000..cdd17854b --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_file_accessor.hpp" +#include "fs_directory_accessor.hpp" +#include "fs_filesystem_accessor.hpp" + +namespace ams::fs::impl { + + namespace { + + template + void Remove(List &list, Iter *desired) { + for (auto it = list.cbegin(); it != list.cend(); it++) { + if (it.operator->() == desired) { + list.erase(it); + return; + } + } + + /* This should never happen. */ + AMS_ABORT(); + } + + Result ValidatePath(const char *mount_name, const char *path) { + const size_t mount_name_len = strnlen(mount_name, MountNameLengthMax); + const size_t path_len = strnlen(path, EntryNameLengthMax); + R_UNLESS(mount_name_len + 1 + path_len <= EntryNameLengthMax, fs::ResultTooLongPath()); + + return ResultSuccess(); + } + + Result ValidateMountName(const char *name) { + R_UNLESS(name[0] != 0, fs::ResultInvalidMountName()); + R_UNLESS(strnlen(name, sizeof(MountName)) < sizeof(MountName), fs::ResultInvalidMountName()); + return ResultSuccess(); + } + + template + Result ValidateNoOpenWriteModeFiles(List &list) { + for (auto it = list.cbegin(); it != list.cend(); it++) { + R_UNLESS((it->GetOpenMode() & OpenMode_Write) == 0, fs::ResultWriteModeFileNotClosed()); + } + return ResultSuccess(); + } + + } + + FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr &&fs, std::unique_ptr &&generator) + : impl(std::move(fs)), mount_name_generator(std::move(generator)), + access_log_enabled(false), data_cache_attachable(false), path_cache_attachable(false), path_cache_attached(false), multi_commit_supported(false) + { + R_ABORT_UNLESS(ValidateMountName(n)); + std::strncpy(this->name.str, n, MountNameLengthMax); + this->name.str[MountNameLengthMax] = 0; + } + + FileSystemAccessor::~FileSystemAccessor() { + std::scoped_lock lk(this->open_list_lock); + + /* TODO: Iterate over list entries. */ + + if (!this->open_file_list.empty()) { R_ABORT_UNLESS(fs::ResultFileNotClosed()); } + if (!this->open_dir_list.empty()) { R_ABORT_UNLESS(fs::ResultDirectoryNotClosed()); } + + if (this->path_cache_attached) { + /* TODO: Invalidate path cache */ + } + } + + Result FileSystemAccessor::GetCommonMountName(char *dst, size_t dst_size) const { + R_UNLESS(this->mount_name_generator != nullptr, fs::ResultPreconditionViolation()); + return this->mount_name_generator->GenerateCommonMountName(dst, dst_size); + } + + std::shared_ptr FileSystemAccessor::GetMultiCommitTarget() { + if (this->multi_commit_supported) { + /* TODO: Support multi commit. */ + AMS_ABORT(); + } + return nullptr; + } + + void FileSystemAccessor::NotifyCloseFile(FileAccessor *f) { + std::scoped_lock lk(this->open_list_lock); + Remove(this->open_file_list, f); + } + + void FileSystemAccessor::NotifyCloseDirectory(DirectoryAccessor *d) { + std::scoped_lock lk(this->open_list_lock); + Remove(this->open_dir_list, d); + } + + Result FileSystemAccessor::CreateFile(const char *path, s64 size, int option) { + R_TRY(ValidatePath(this->name.str, path)); + if (this->path_cache_attached) { + /* TODO: Path cache */ + R_TRY(this->impl->CreateFile(path, size, option)); + } else { + R_TRY(this->impl->CreateFile(path, size, option)); + } + return ResultSuccess(); + } + + Result FileSystemAccessor::DeleteFile(const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->DeleteFile(path); + } + + Result FileSystemAccessor::CreateDirectory(const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->CreateDirectory(path); + } + + Result FileSystemAccessor::DeleteDirectory(const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->DeleteDirectory(path); + } + + Result FileSystemAccessor::DeleteDirectoryRecursively(const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->DeleteDirectoryRecursively(path); + } + + Result FileSystemAccessor::RenameFile(const char *old_path, const char *new_path) { + R_TRY(ValidatePath(this->name.str, old_path)); + R_TRY(ValidatePath(this->name.str, new_path)); + if (this->path_cache_attached) { + /* TODO: Path cache */ + R_TRY(this->impl->RenameFile(old_path, new_path)); + } else { + R_TRY(this->impl->RenameFile(old_path, new_path)); + } + return ResultSuccess(); + } + + Result FileSystemAccessor::RenameDirectory(const char *old_path, const char *new_path) { + R_TRY(ValidatePath(this->name.str, old_path)); + R_TRY(ValidatePath(this->name.str, new_path)); + if (this->path_cache_attached) { + /* TODO: Path cache */ + R_TRY(this->impl->RenameDirectory(old_path, new_path)); + } else { + R_TRY(this->impl->RenameDirectory(old_path, new_path)); + } + return ResultSuccess(); + } + + Result FileSystemAccessor::GetEntryType(DirectoryEntryType *out, const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->GetEntryType(out, path); + } + + Result FileSystemAccessor::OpenFile(std::unique_ptr *out_file, const char *path, OpenMode mode) { + R_TRY(ValidatePath(this->name.str, path)); + + std::unique_ptr file; + R_TRY(this->impl->OpenFile(std::addressof(file), path, mode)); + + auto accessor = new FileAccessor(std::move(file), this, mode); + R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInFileSystemAccessorA()); + + { + std::scoped_lock lk(this->open_list_lock); + this->open_file_list.push_back(*accessor); + } + + if (this->path_cache_attached) { + if (mode & OpenMode_Append) { + /* TODO: Append Path cache */ + } else { + /* TODO: Non-append path cache */ + } + } + + out_file->reset(accessor); + return ResultSuccess(); + } + + Result FileSystemAccessor::OpenDirectory(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) { + R_TRY(ValidatePath(this->name.str, path)); + + std::unique_ptr dir; + R_TRY(this->impl->OpenDirectory(std::addressof(dir), path, mode)); + + auto accessor = new DirectoryAccessor(std::move(dir), *this); + R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInFileSystemAccessorB()); + + { + std::scoped_lock lk(this->open_list_lock); + this->open_dir_list.push_back(*accessor); + } + + out_dir->reset(accessor); + return ResultSuccess(); + } + + Result FileSystemAccessor::Commit() { + { + std::scoped_lock lk(this->open_list_lock); + R_ABORT_UNLESS(ValidateNoOpenWriteModeFiles(this->open_file_list)); + } + return this->impl->Commit(); + } + + Result FileSystemAccessor::GetFreeSpaceSize(s64 *out, const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->GetFreeSpaceSize(out, path); + } + + Result FileSystemAccessor::GetTotalSpaceSize(s64 *out, const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->GetTotalSpaceSize(out, path); + } + + Result FileSystemAccessor::CleanDirectoryRecursively(const char *path) { + R_TRY(ValidatePath(this->name.str, path)); + return this->impl->CleanDirectoryRecursively(path); + } + + Result FileSystemAccessor::GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) { + return this->impl->GetFileTimeStampRaw(out, path); + } + + Result FileSystemAccessor::QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) { + return this->impl->QueryEntry(dst, dst_size, src, src_size, query, path); + } + + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp new file mode 100644 index 000000000..958d8cd78 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include "fs_mount_name.hpp" + +namespace ams::fs::impl { + + class FileAccessor; + class DirectoryAccessor; + + class FileSystemAccessor : public util::IntrusiveListBaseNode, public Newable { + NON_COPYABLE(FileSystemAccessor); + friend class FileAccessor; + friend class DirectoryAccessor; + private: + using FileList = util::IntrusiveListBaseTraits::ListType; + using DirList = util::IntrusiveListBaseTraits::ListType; + private: + MountName name; + std::unique_ptr impl; + FileList open_file_list; + DirList open_dir_list; + os::Mutex open_list_lock; + std::unique_ptr mount_name_generator; + bool access_log_enabled; + bool data_cache_attachable; + bool path_cache_attachable; + bool path_cache_attached; + bool multi_commit_supported; + public: + FileSystemAccessor(const char *name, std::unique_ptr &&fs, std::unique_ptr &&generator = nullptr); + virtual ~FileSystemAccessor(); + + Result CreateFile(const char *path, s64 size, int option); + Result DeleteFile(const char *path); + Result CreateDirectory(const char *path); + Result DeleteDirectory(const char *path); + Result DeleteDirectoryRecursively(const char *path); + Result RenameFile(const char *old_path, const char *new_path); + Result RenameDirectory(const char *old_path, const char *new_path); + Result GetEntryType(DirectoryEntryType *out, const char *path); + Result OpenFile(std::unique_ptr *out_file, const char *path, OpenMode mode); + Result OpenDirectory(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode); + Result Commit(); + Result GetFreeSpaceSize(s64 *out, const char *path); + Result GetTotalSpaceSize(s64 *out, const char *path); + Result CleanDirectoryRecursively(const char *path); + Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path); + Result QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path); + + const char *GetName() const { return this->name.str; } + Result GetCommonMountName(char *dst, size_t dst_size) const; + + void SetAccessLogEnabled(bool en) { this->access_log_enabled = en; } + void SetFileDataCacheAttachable(bool en) { this->data_cache_attachable = en; } + void SetPathBasedFileDataCacheAttachable(bool en) { this->path_cache_attachable = en; } + void SetMultiCommitSupported(bool en) { this->multi_commit_supported = en; } + + bool IsAccessLogEnabled() const { return this->access_log_enabled; } + bool IsFileDataCacheAttachable() const { return this->data_cache_attachable; } + bool IsPathBasedFileDataCacheAttachable() const { return this->path_cache_attachable; } + + void AttachPathBasedFileDataCache() { + if (this->IsPathBasedFileDataCacheAttachable()) { + this->path_cache_attached = true; + } + } + + void DetachPathBasedFileDataCache() { + this->path_cache_attached = false; + } + + std::shared_ptr GetMultiCommitTarget(); + private: + void NotifyCloseFile(FileAccessor *f); + void NotifyCloseDirectory(DirectoryAccessor *d); + }; + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp new file mode 100644 index 000000000..8f77d3fe2 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs { + + struct MountName { + char str[MountNameLengthMax + 1]; + }; + static_assert(std::is_pod::value); + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_table.cpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.cpp new file mode 100644 index 000000000..fde663d26 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_mount_table.hpp" + +namespace ams::fs::impl { + + namespace { + + bool MatchesName(const FileSystemAccessor &accessor, const char *name) { + return std::strncmp(accessor.GetName(), name, sizeof(MountName)) == 0; + } + + } + + bool MountTable::CanAcceptMountName(const char *name) { + for (const auto &fs : this->fs_list) { + if (MatchesName(fs, name)) { + return false; + } + } + return true; + } + + Result MountTable::Mount(std::unique_ptr &&fs) { + std::scoped_lock lk(this->mutex); + + R_UNLESS(this->CanAcceptMountName(fs->GetName()), fs::ResultMountNameAlreadyExists()); + + this->fs_list.push_back(*fs.release()); + return ResultSuccess(); + } + + Result MountTable::Find(FileSystemAccessor **out, const char *name) { + std::scoped_lock lk(this->mutex); + + for (auto &fs : this->fs_list) { + if (MatchesName(fs, name)) { + *out = std::addressof(fs); + return ResultSuccess(); + } + } + + return fs::ResultNotMounted(); + } + + void MountTable::Unmount(const char *name) { + std::scoped_lock lk(this->mutex); + + for (auto it = this->fs_list.cbegin(); it != this->fs_list.cend(); it++) { + if (MatchesName(*it, name)) { + auto p = std::addressof(*it); + this->fs_list.erase(it); + delete p; + return; + } + } + + R_ABORT_UNLESS(fs::ResultNotMounted()); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp new file mode 100644 index 000000000..23145d840 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "fs_filesystem_accessor.hpp" + +namespace ams::fs::impl { + + class MountTable : public Newable { + NON_COPYABLE(MountTable); + NON_MOVEABLE(MountTable); + private: + using FileSystemList = util::IntrusiveListBaseTraits::ListType; + private: + FileSystemList fs_list; + os::Mutex mutex; + public: + constexpr MountTable() : fs_list(), mutex() { /* ... */ } + private: + bool CanAcceptMountName(const char *name); + public: + Result Mount(std::unique_ptr &&fs); + Result Find(FileSystemAccessor **out, const char *name); + void Unmount(const char *name); + }; + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp new file mode 100644 index 000000000..8f94eb750 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_filesystem_accessor.hpp" +#include "fs_mount_utils.hpp" +#include "fs_user_mount_table.hpp" + +namespace ams::fs::impl { + + namespace { + + const char *FindMountNameDriveSeparator(const char *path) { + for (const char *cur = path; cur < path + MountNameLengthMax + 1; cur++) { + if (PathTool::IsDriveSeparator(*cur)) { + return cur; + } else if (PathTool::IsNullTerminator(*cur)) { + break; + } + } + return nullptr; + } + + Result GetMountNameAndSubPath(MountName *out_mount_name, const char **out_sub_path, const char *path) { + /* Handle the Host-path case. */ + if (PathTool::IsWindowsAbsolutePath(path) || PathTool::IsUnc(path)) { + std::strncpy(out_mount_name->str, HostRootFileSystemMountName, MountNameLengthMax); + out_mount_name->str[MountNameLengthMax] = '\x00'; + return ResultSuccess(); + } + + /* Locate the drive separator. */ + const char *drive_separator = FindMountNameDriveSeparator(path); + R_UNLESS(drive_separator != nullptr, fs::ResultInvalidMountName()); + + /* Ensure the mount name isn't too long. */ + const size_t len = drive_separator - path; + R_UNLESS(len <= MountNameLengthMax, fs::ResultInvalidMountName()); + + /* Ensure the result sub-path is valid. */ + const char *sub_path = drive_separator + 1; + R_UNLESS(!PathTool::IsNullTerminator(sub_path[0]), fs::ResultInvalidMountName()); + R_UNLESS(PathTool::IsAnySeparator(sub_path[0]), fs::ResultInvalidPathFormat()); + + /* Set output. */ + std::memcpy(out_mount_name->str, path, len); + out_mount_name->str[len] = StringTraits::NullTerminator; + *out_sub_path = sub_path; + return ResultSuccess(); + } + + } + + bool IsValidMountName(const char *name) { + if (PathTool::IsNullTerminator(*name)) { + return false; + } + + if (PathTool::IsWindowsDriveCharacter(name[0]) && PathTool::IsNullTerminator(name[1])) { + return false; + } + + size_t len = 0; + for (const char *cur = name; !PathTool::IsNullTerminator(*cur); cur++) { + if (PathTool::IsDriveSeparator(*cur) || PathTool::IsSeparator(*cur)) { + return false; + } + + if ((++len) > MountNameLengthMax) { + return false; + } + } + + /* TODO: N validates that the mount name decodes via utf-8 here. */ + return true; + } + + bool IsWindowsDrive(const char *name) { + return PathTool::IsWindowsAbsolutePath(name); + } + + bool IsReservedMountName(const char *name) { + return name[0] == ReservedMountNamePrefixCharacter; + } + + Result CheckMountName(const char *name) { + R_TRY(CheckMountNameAllowingReserved(name)); + R_UNLESS(!impl::IsReservedMountName(name), fs::ResultInvalidMountName()); + return ResultSuccess(); + } + + Result CheckMountNameAllowingReserved(const char *name) { + R_UNLESS(name != nullptr, fs::ResultInvalidMountName()); + R_UNLESS(impl::IsValidMountName(name), fs::ResultInvalidMountName()); + return ResultSuccess(); + } + + Result FindFileSystem(FileSystemAccessor **out_accessor, const char **out_sub_path, const char *path) { + R_UNLESS(out_accessor != nullptr, fs::ResultUnexpectedInFindFileSystemA()); + R_UNLESS(out_sub_path != nullptr, fs::ResultUnexpectedInFindFileSystemA()); + R_UNLESS(path != nullptr, fs::ResultNullptrArgument()); + + R_UNLESS(strncmp(path, HostRootFileSystemMountName, strnlen(HostRootFileSystemMountName, sizeof(MountName))) != 0, fs::ResultNotMounted()); + + MountName mount_name; + R_TRY(GetMountNameAndSubPath(std::addressof(mount_name), out_sub_path, path)); + + return impl::Find(out_accessor, mount_name.str); + } + +} + +namespace ams::fs { + + namespace { + + Result UnmountImpl(const char *name) { + impl::FileSystemAccessor *accessor; + R_TRY(impl::Find(std::addressof(accessor), name)); + + if (accessor->IsFileDataCacheAttachable()) { + /* TODO: Data cache purge */ + } + + impl::Unregister(name); + return ResultSuccess(); + } + + } + + Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *src) { + /* Ensure neither argument is nullptr. */ + R_UNLESS(dst != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(src != nullptr, fs::ResultNullptrArgument()); + + /* Get the mount name and sub path for the path. */ + MountName mount_name; + const char *sub_path; + R_TRY(impl::GetMountNameAndSubPath(std::addressof(mount_name), std::addressof(sub_path), src)); + + impl::FileSystemAccessor *accessor; + R_TRY(impl::Find(std::addressof(accessor), mount_name.str)); + R_TRY(accessor->GetCommonMountName(dst, dst_size)); + + const auto mount_name_len = strnlen(dst, dst_size); + const auto common_path_len = std::snprintf(dst + mount_name_len, dst_size - mount_name_len, "%s", sub_path); + + R_UNLESS(static_cast(common_path_len) < dst_size - mount_name_len, fs::ResultTooLongPath()); + return ResultSuccess(); + } + + void Unmount(const char *mount_name) { + R_ABORT_UNLESS(UnmountImpl(mount_name)); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp new file mode 100644 index 000000000..1b174f5c0 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + class FileSystemAccessor; + + Result FindFileSystem(FileSystemAccessor **out_accessor, const char **out_sub_path, const char *path); + + bool IsWindowsDrive(const char *name); + bool IsReservedMountName(const char *name); + Result CheckMountName(const char *name); + Result CheckMountNameAllowingReserved(const char *name); + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_registrar.cpp b/libraries/libstratosphere/source/fs/fsa/fs_registrar.cpp new file mode 100644 index 000000000..d416bfd69 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_registrar.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_filesystem_accessor.hpp" +#include "fs_user_mount_table.hpp" + +namespace ams::fs::fsa { + + Result Register(const char *name, std::unique_ptr &&fs) { + auto accessor = std::make_unique(name, std::move(fs)); + R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInRegisterA()); + + return impl::Register(std::move(accessor)); + } + + Result Register(const char *name, std::unique_ptr &&fs, std::unique_ptr &&generator) { + auto accessor = std::make_unique(name, std::move(fs), std::move(generator)); + R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInRegisterB()); + + return impl::Register(std::move(accessor)); + } + + Result Register(const char *name, std::unique_ptr &&fs, std::unique_ptr &&generator, bool use_data_cache, bool use_path_cache, bool support_multi_commit) { + auto accessor = std::make_unique(name, std::move(fs), std::move(generator)); + R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInRegisterB()); + + accessor->SetFileDataCacheAttachable(use_data_cache); + accessor->SetPathBasedFileDataCacheAttachable(use_path_cache); + accessor->SetMultiCommitSupported(support_multi_commit); + + return impl::Register(std::move(accessor)); + } + + void Unregister(const char *name) { + impl::Unregister(name); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_directory.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_directory.cpp new file mode 100644 index 000000000..e29957c60 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_directory.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_directory_accessor.hpp" +#include "fs_filesystem_accessor.hpp" + +namespace ams::fs { + + namespace { + + ALWAYS_INLINE impl::DirectoryAccessor *Get(DirectoryHandle handle) { + return reinterpret_cast(handle.handle); + } + + } + + Result ReadDirectory(s64 *out_count, DirectoryEntry *out_entries, DirectoryHandle handle, s64 max_entries) { + return Get(handle)->Read(out_count, out_entries, max_entries); + } + + Result GetDirectoryEntryCount(s64 *out, DirectoryHandle handle) { + return Get(handle)->GetEntryCount(out); + } + + void CloseDirectory(DirectoryHandle handle) { + delete Get(handle); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_file.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_file.cpp new file mode 100644 index 000000000..7e2a816ca --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_file.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_file_accessor.hpp" +#include "fs_filesystem_accessor.hpp" + +namespace ams::fs { + + namespace { + + ALWAYS_INLINE impl::FileAccessor *Get(FileHandle handle) { + return reinterpret_cast(handle.handle); + } + + } + + Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) { + size_t read_size; + R_TRY(ReadFile(std::addressof(read_size), handle, offset, buffer, size, option)); + R_UNLESS(read_size == size, fs::ResultOutOfRange()); + return ResultSuccess(); + } + + Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size) { + return ReadFile(handle, offset, buffer, size, ReadOption()); + } + + Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) { + return Get(handle)->Read(out, offset, buffer, size, option); + } + + Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size) { + return ReadFile(out, handle, offset, buffer, size, ReadOption()); + } + + Result GetFileSize(s64 *out, FileHandle handle) { + return Get(handle)->GetSize(out); + } + + Result FlushFile(FileHandle handle) { + return Get(handle)->Flush(); + } + + Result WriteFile(FileHandle handle, s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) { + return Get(handle)->Write(offset, buffer, size, option); + } + + Result SetFileSize(FileHandle handle, s64 size) { + return Get(handle)->SetSize(size); + } + + int GetFileOpenMode(FileHandle handle) { + return Get(handle)->GetOpenMode(); + } + + void CloseFile(FileHandle handle) { + delete Get(handle); + } + + Result QueryRange(QueryRangeInfo *out, FileHandle handle, s64 offset, s64 size) { + return Get(handle)->OperateRange(out, sizeof(*out), OperationId::QueryRange, offset, size, nullptr, 0); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp new file mode 100644 index 000000000..b4c77cda7 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_filesystem_accessor.hpp" +#include "fs_file_accessor.hpp" +#include "fs_directory_accessor.hpp" +#include "fs_mount_utils.hpp" +#include "fs_user_mount_table.hpp" + +namespace ams::fs { + + Result CreateFile(const char *path, s64 size) { + return CreateFile(path, size, 0); + } + + Result CreateFile(const char* path, s64 size, int option) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->CreateFile(sub_path, size, option); + } + + Result DeleteFile(const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->DeleteFile(sub_path); + } + + Result CreateDirectory(const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->CreateDirectory(sub_path); + } + + Result DeleteDirectory(const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->DeleteDirectory(sub_path); + } + + Result DeleteDirectoryRecursively(const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->DeleteDirectoryRecursively(sub_path); + } + + Result RenameFile(const char *old_path, const char *new_path) { + impl::FileSystemAccessor *old_accessor; + impl::FileSystemAccessor *new_accessor; + const char *old_sub_path; + const char *new_sub_path; + R_TRY(impl::FindFileSystem(std::addressof(old_accessor), std::addressof(old_sub_path), old_path)); + R_TRY(impl::FindFileSystem(std::addressof(new_accessor), std::addressof(new_sub_path), new_path)); + + R_UNLESS(old_accessor == new_accessor, fs::ResultRenameToOtherFileSystem()); + return old_accessor->RenameFile(old_sub_path, new_sub_path); + } + + Result RenameDirectory(const char *old_path, const char *new_path) { + impl::FileSystemAccessor *old_accessor; + impl::FileSystemAccessor *new_accessor; + const char *old_sub_path; + const char *new_sub_path; + R_TRY(impl::FindFileSystem(std::addressof(old_accessor), std::addressof(old_sub_path), old_path)); + R_TRY(impl::FindFileSystem(std::addressof(new_accessor), std::addressof(new_sub_path), new_path)); + + R_UNLESS(old_accessor == new_accessor, fs::ResultRenameToOtherFileSystem()); + return old_accessor->RenameDirectory(old_sub_path, new_sub_path); + } + + Result GetEntryType(DirectoryEntryType *out, const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->GetEntryType(out, sub_path); + } + + Result OpenFile(FileHandle *out_file, const char *path, int mode) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + R_UNLESS(out_file != nullptr, fs::ResultNullptrArgument()); + + std::unique_ptr file_accessor; + R_TRY(accessor->OpenFile(std::addressof(file_accessor), sub_path, static_cast(mode))); + + out_file->handle = file_accessor.release(); + return ResultSuccess(); + } + + Result OpenDirectory(DirectoryHandle *out_dir, const char *path, int mode) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument()); + + std::unique_ptr dir_accessor; + R_TRY(accessor->OpenDirectory(std::addressof(dir_accessor), sub_path, static_cast(mode))); + + out_dir->handle = dir_accessor.release(); + return ResultSuccess(); + } + + Result CleanDirectoryRecursively(const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->CleanDirectoryRecursively(sub_path); + } + + Result GetFreeSpaceSize(s64 *out, const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->GetFreeSpaceSize(out, sub_path); + } + + Result GetTotalSpaceSize(s64 *out, const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->GetTotalSpaceSize(out, sub_path); + } + + Result SetConcatenationFileAttribute(const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->QueryEntry(nullptr, 0, nullptr, 0, fsa::QueryId::SetConcatenationFileAttribute, sub_path); + } + + Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) { + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return accessor->GetFileTimeStampRaw(out, sub_path); + } + + Result OpenFile(FileHandle *out, std::unique_ptr &&file, int mode) { + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + + auto file_accessor = std::make_unique(std::move(file), nullptr, static_cast(mode)); + R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInNew()); + out->handle = file_accessor.release(); + + return ResultSuccess(); + } + + namespace { + + Result CommitImpl(const char *path) { + impl::FileSystemAccessor *accessor; + R_TRY(impl::Find(std::addressof(accessor), path)); + + return accessor->Commit(); + } + + } + + Result Commit(const char *path) { + return CommitImpl(path); + } + + Result CommitSaveData(const char *path) { + return CommitImpl(path); + } + + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.cpp new file mode 100644 index 000000000..b9c50b45b --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "fs_user_mount_table.hpp" +#include "fs_mount_table.hpp" +#include "fs_filesystem_accessor.hpp" + +namespace ams::fs::impl { + + namespace { + + MountTable g_mount_table; + + } + + Result Register(std::unique_ptr &&fs) { + return g_mount_table.Mount(std::move(fs)); + } + + Result Find(FileSystemAccessor **out, const char *name) { + return g_mount_table.Find(out, name); + } + + void Unregister(const char *name) { + g_mount_table.Unmount(name); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.hpp b/libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.hpp new file mode 100644 index 000000000..456051599 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_mount_table.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + class FileSystemAccessor; + + Result Register(std::unique_ptr &&fs); + Result Find(FileSystemAccessor **out, const char *name); + void Unregister(const char *name); + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp b/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp index 9cc3d15db..f867917cc 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_directory_savedata_filesystem.cpp @@ -178,7 +178,7 @@ namespace ams::fssystem { Result DirectorySaveDataFileSystem::CopySaveFromFileSystem(fs::fsa::IFileSystem *save_fs) { /* If the input save is null, there's nothing to copy. */ - R_UNLESS(save_fs != nullptr, ResultSuccess()); + R_SUCCEED_IF(save_fs == nullptr); /* Get a work buffer to work with. */ std::unique_ptr work_buf; diff --git a/libraries/libstratosphere/source/hid/hid_api.cpp b/libraries/libstratosphere/source/hid/hid_api.cpp index 909202928..e5d3bfc7d 100644 --- a/libraries/libstratosphere/source/hid/hid_api.cpp +++ b/libraries/libstratosphere/source/hid/hid_api.cpp @@ -35,7 +35,7 @@ namespace ams::hid { Result EnsureHidInitialized() { if (!g_initialized_hid) { if (!serviceIsActive(hidGetServiceSession())) { - if (!pm::info::HasLaunchedProgram(ncm::ProgramId::Hid)) { + if (!pm::info::HasLaunchedProgram(ncm::SystemProgramId::Hid)) { return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); } InitializeHid(); diff --git a/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp b/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp index 6a980f3ad..540ef3608 100644 --- a/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp +++ b/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp @@ -180,11 +180,9 @@ namespace ams::kvdb { Result FileKeyValueStore::InitializeWithCache(const char *dir, void *cache_buffer, size_t cache_buffer_size, size_t cache_capacity) { /* Ensure that the passed path is a directory. */ - { - struct stat st; - R_UNLESS(stat(dir, &st) == 0, fs::ResultPathNotFound()); - R_UNLESS((S_ISDIR(st.st_mode)), fs::ResultPathNotFound()); - } + fs::DirectoryEntryType entry_type; + R_TRY(fs::GetEntryType(std::addressof(entry_type), dir)); + R_UNLESS(entry_type == fs::DirectoryEntryType_Directory, fs::ResultPathNotFound()); /* Set path. */ this->dir_path.Set(dir); @@ -210,24 +208,22 @@ namespace ams::kvdb { } /* Open the value file. */ - FILE *fp = fopen(this->GetPath(key, key_size), "rb"); - if (fp == nullptr) { - R_TRY_CATCH(fsdevGetLastResult()) { - R_CONVERT(fs::ResultPathNotFound, ResultKeyNotFound()) - } R_END_TRY_CATCH; - } - ON_SCOPE_EXIT { fclose(fp); }; + fs::FileHandle file; + R_TRY_CATCH(fs::OpenFile(std::addressof(file), this->GetPath(key, key_size), fs::OpenMode_Read)) { + R_CONVERT(fs::ResultPathNotFound, ResultKeyNotFound()); + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { fs::CloseFile(file); }; /* Get the value size. */ - fseek(fp, 0, SEEK_END); - const size_t value_size = ftell(fp); - fseek(fp, 0, SEEK_SET); + s64 file_size; + R_TRY(fs::GetFileSize(std::addressof(file_size), file)); /* Ensure there's enough space for the value. */ - R_UNLESS(value_size <= max_out_size, ResultBufferInsufficient()); + R_UNLESS(file_size <= static_cast(max_out_size), ResultBufferInsufficient()); /* Read the value. */ - R_UNLESS(fread(out_value, value_size, 1, fp) == 1, fsdevGetLastResult()); + const size_t value_size = static_cast(value_size); + R_TRY(fs::ReadFile(file, 0, out_value, value_size)); *out_size = value_size; /* Cache the newly read value. */ @@ -251,17 +247,17 @@ namespace ams::kvdb { } /* Open the value file. */ - FILE *fp = fopen(this->GetPath(key, key_size), "rb"); - if (fp == nullptr) { - R_TRY_CATCH(fsdevGetLastResult()) { - R_CONVERT(fs::ResultPathNotFound, ResultKeyNotFound()) - } R_END_TRY_CATCH; - } - ON_SCOPE_EXIT { fclose(fp); }; + fs::FileHandle file; + R_TRY_CATCH(fs::OpenFile(std::addressof(file), this->GetPath(key, key_size), fs::OpenMode_Read)) { + R_CONVERT(fs::ResultPathNotFound, ResultKeyNotFound()); + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { fs::CloseFile(file); }; /* Get the value size. */ - fseek(fp, 0, SEEK_END); - *out_size = ftell(fp); + s64 file_size; + R_TRY(fs::GetFileSize(std::addressof(file_size), file)); + + *out_size = static_cast(file_size); return ResultSuccess(); } @@ -278,18 +274,18 @@ namespace ams::kvdb { /* Delete the file, if it exists. Don't check result, since it's okay if it's already deleted. */ auto key_path = this->GetPath(key, key_size); - std::remove(key_path); + fs::DeleteFile(key_path); + + /* Create the new value file. */ + R_TRY(fs::CreateFile(key_path, value_size)); /* Open the value file. */ - FILE *fp = fopen(key_path, "wb"); - R_UNLESS(fp != nullptr, fsdevGetLastResult()); - ON_SCOPE_EXIT { fclose(fp); }; + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), key_path, fs::OpenMode_Write)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; - /* Write the value file. */ - R_UNLESS(fwrite(value, value_size, 1, fp) == 1, fsdevGetLastResult()); - - /* Flush the value file. */ - fflush(fp); + /* Write the value file and flush. */ + R_TRY(fs::WriteFile(file, 0, value, value_size, fs::WriteOption::Flush)); return ResultSuccess(); } @@ -306,11 +302,9 @@ namespace ams::kvdb { } /* Remove the file. */ - if (std::remove(this->GetPath(key, key_size)) != 0) { - R_TRY_CATCH(fsdevGetLastResult()) { - R_CONVERT(fs::ResultPathNotFound, ResultKeyNotFound()) - } R_END_TRY_CATCH; - } + R_TRY_CATCH(fs::DeleteFile(this->GetPath(key, key_size))) { + R_CONVERT(fs::ResultPathNotFound, ResultKeyNotFound()) + } R_END_TRY_CATCH; return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.cpp b/libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.cpp new file mode 100644 index 000000000..bf3ceb184 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "lr_add_on_content_location_resolver_impl.hpp" + +namespace ams::lr { + + Result AddOnContentLocationResolverImpl::ResolveAddOnContentPath(sf::Out out, ncm::DataId id) { + /* Find a storage that contains the given program id. */ + ncm::StorageId storage_id = ncm::StorageId::None; + R_UNLESS(this->registered_storages.Find(&storage_id, id), lr::ResultAddOnContentNotFound()); + + /* Obtain a content meta database for the storage id. */ + ncm::ContentMetaDatabase content_meta_database; + R_TRY(ncm::OpenContentMetaDatabase(&content_meta_database, storage_id)); + + /* Find the latest data content id for the given program id. */ + ncm::ContentId data_content_id; + R_TRY(content_meta_database.GetLatestData(&data_content_id, id)); + + /* Obtain a content storage for the storage id. */ + ncm::ContentStorage content_storage; + R_TRY(ncm::OpenContentStorage(&content_storage, storage_id)); + + /* Get the path of the data content. */ + static_assert(sizeof(lr::Path) == sizeof(ncm::Path)); + content_storage.GetPath(reinterpret_cast(out.GetPointer()), data_content_id); + + return ResultSuccess(); + } + + Result AddOnContentLocationResolverImpl::RegisterAddOnContentStorageDeprecated(ncm::DataId id, ncm::StorageId storage_id) { + /* Register storage for the given program id. 2.0.0-8.1.0 did not require an owner application id. */ + R_UNLESS(this->registered_storages.Register(id, storage_id, ncm::InvalidApplicationId), lr::ResultTooManyRegisteredPaths()); + return ResultSuccess(); + } + + Result AddOnContentLocationResolverImpl::RegisterAddOnContentStorage(ncm::DataId id, ncm::ApplicationId application_id, ncm::StorageId storage_id) { + /* Register storage for the given program id and owner application. */ + R_UNLESS(this->registered_storages.Register(id, storage_id, application_id), lr::ResultTooManyRegisteredPaths()); + return ResultSuccess(); + } + + Result AddOnContentLocationResolverImpl::UnregisterAllAddOnContentPath() { + this->registered_storages.Clear(); + return ResultSuccess(); + } + + Result AddOnContentLocationResolverImpl::RefreshApplicationAddOnContent(const sf::InArray &ids) { + if (ids.GetSize() == 0) { + /* Clear all registered storages. */ + this->registered_storages.Clear(); + } else { + /* Clear all registered storages excluding the provided program ids. */ + this->registered_storages.ClearExcluding(reinterpret_cast(ids.GetPointer()), ids.GetSize()); + } + + return ResultSuccess(); + } + + Result AddOnContentLocationResolverImpl::UnregisterApplicationAddOnContent(ncm::ApplicationId id) { + /* Remove entries belonging to the provided application. */ + this->registered_storages.UnregisterOwnerProgram(id); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.hpp new file mode 100644 index 000000000..c80238a3c --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_add_on_content_location_resolver_impl.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include "lr_location_redirector.hpp" +#include "lr_registered_data.hpp" + +namespace ams::lr { + + class AddOnContentLocationResolverImpl : public IAddOnContentLocationResolver { + private: + /* Storage for RegisteredData entries by data id. */ + RegisteredStorages registered_storages; + public: + AddOnContentLocationResolverImpl() : registered_storages(hos::GetVersion() < hos::Version_900 ? 0x800 : 0x2) { /* ... */ } + + /* Actual commands. */ + virtual Result ResolveAddOnContentPath(sf::Out out, ncm::DataId id) override; + virtual Result RegisterAddOnContentStorageDeprecated(ncm::DataId id, ncm::StorageId storage_id) override; + virtual Result RegisterAddOnContentStorage(ncm::DataId id, ncm::ApplicationId application_id, ncm::StorageId storage_id) override; + virtual Result UnregisterAllAddOnContentPath() override; + virtual Result RefreshApplicationAddOnContent(const sf::InArray &ids) override; + virtual Result UnregisterApplicationAddOnContent(ncm::ApplicationId id) override; + }; + +} diff --git a/libraries/libstratosphere/source/lr/lr_api.cpp b/libraries/libstratosphere/source/lr/lr_api.cpp new file mode 100644 index 000000000..36a7cb813 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_api.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "lr_remote_location_resolver_impl.hpp" +#include "lr_remote_registered_location_resolver_impl.hpp" + +namespace ams::lr { + + namespace { + + bool g_initialized; + + } + + void Initialize() { + AMS_ASSERT(!g_initialized); + R_ABORT_UNLESS(lrInitialize()); + g_initialized = true; + } + + void Finalize() { + AMS_ASSERT(g_initialized); + lrExit(); + g_initialized = false; + } + + + Result OpenLocationResolver(LocationResolver *out, ncm::StorageId storage_id) { + LrLocationResolver lr; + R_TRY(lrOpenLocationResolver(static_cast(storage_id), std::addressof(lr))); + + *out = LocationResolver(std::make_shared(lr)); + return ResultSuccess(); + } + + Result OpenRegisteredLocationResolver(RegisteredLocationResolver *out) { + LrRegisteredLocationResolver lr; + R_TRY(lrOpenRegisteredLocationResolver(std::addressof(lr))); + + *out = RegisteredLocationResolver(std::make_shared(lr)); + return ResultSuccess(); + } + + Result OpenAddOnContentLocationResolver(AddOnContentLocationResolver *out) { + /* TODO: libnx binding */ + AMS_ABORT(); + } + + Result RefreshLocationResolver(ncm::StorageId storage_id) { + /* TODO: libnx binding */ + AMS_ABORT(); + } +} diff --git a/libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.cpp b/libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.cpp new file mode 100644 index 000000000..49df0c001 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "lr_content_location_resolver_impl.hpp" + +namespace ams::lr { + + ContentLocationResolverImpl::~ContentLocationResolverImpl() { + this->ClearRedirections(); + } + + /* Helper function. */ + void ContentLocationResolverImpl::GetContentStoragePath(Path *out, ncm::ContentId content_id) { + static_assert(sizeof(lr::Path) == sizeof(ncm::Path)); + this->content_storage.GetPath(reinterpret_cast(out), content_id); + } + + Result ContentLocationResolverImpl::ResolveProgramPath(sf::Out out, ncm::ProgramId id) { + /* Use a redirection if present. */ + R_SUCCEED_IF(this->program_redirector.FindRedirection(out.GetPointer(), id)); + + /* Find the latest program content for the program id. */ + ncm::ContentId program_content_id; + R_TRY_CATCH(this->content_meta_database.GetLatestProgram(&program_content_id, id)) { + R_CONVERT(ncm::ResultContentMetaNotFound, lr::ResultProgramNotFound()) + } R_END_TRY_CATCH; + + /* Obtain the content path. */ + this->GetContentStoragePath(out.GetPointer(), program_content_id); + + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectProgramPath(const Path &path, ncm::ProgramId id) { + this->program_redirector.SetRedirection(id, path); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(this->app_control_redirector.FindRedirection(out.GetPointer(), id), lr::ResultControlNotFound()); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(this->html_docs_redirector.FindRedirection(out.GetPointer(), id), lr::ResultHtmlDocumentNotFound()); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::ResolveDataPath(sf::Out out, ncm::DataId id) { + /* Find the latest data content for the program id. */ + ncm::ContentId data_content_id; + R_TRY(this->content_meta_database.GetLatestData(&data_content_id, id)); + + /* Obtain the content path. */ + this->GetContentStoragePath(out.GetPointer(), data_content_id); + + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) { + this->app_control_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->app_control_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) { + this->html_docs_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->html_docs_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(this->legal_info_redirector.FindRedirection(out.GetPointer(), id), lr::ResultLegalInformationNotFound()); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) { + this->legal_info_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->legal_info_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::Refresh() { + /* Obtain content meta database and content storage objects for this resolver's storage. */ + ncm::ContentMetaDatabase meta_db; + ncm::ContentStorage storage; + R_TRY(ncm::OpenContentMetaDatabase(&meta_db, this->storage_id)); + R_TRY(ncm::OpenContentStorage(&storage, this->storage_id)); + + /* Store the acquired objects. */ + this->content_meta_database = std::move(meta_db); + this->content_storage = std::move(storage); + + /* Remove any existing redirections. */ + this->ClearRedirections(); + + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationProgramPathDeprecated(const Path &path, ncm::ProgramId id) { + this->program_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->program_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::ClearApplicationRedirectionDeprecated() { + this->ClearRedirections(RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::ClearApplicationRedirection(const sf::InArray &excluding_ids) { + this->ClearRedirections(excluding_ids.GetPointer(), excluding_ids.GetSize()); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::EraseProgramRedirection(ncm::ProgramId id) { + this->program_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::EraseApplicationControlRedirection(ncm::ProgramId id) { + this->app_control_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::EraseApplicationHtmlDocumentRedirection(ncm::ProgramId id) { + this->html_docs_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::EraseApplicationLegalInformationRedirection(ncm::ProgramId id) { + this->legal_info_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::ResolveProgramPathForDebug(sf::Out out, ncm::ProgramId id) { + /* Use a redirection if present. */ + R_SUCCEED_IF(this->debug_program_redirector.FindRedirection(out.GetPointer(), id)); + + /* Otherwise find the path for the program id. */ + R_TRY_CATCH(this->ResolveProgramPath(out.GetPointer(), id)) { + R_CONVERT(ResultProgramNotFound, lr::ResultDebugProgramNotFound()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectProgramPathForDebug(const Path &path, ncm::ProgramId id) { + this->debug_program_redirector.SetRedirection(id, path); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationProgramPathForDebugDeprecated(const Path &path, ncm::ProgramId id) { + this->debug_program_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::RedirectApplicationProgramPathForDebug(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->debug_program_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result ContentLocationResolverImpl::EraseProgramRedirectionForDebug(ncm::ProgramId id) { + this->debug_program_redirector.EraseRedirection(id); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.hpp new file mode 100644 index 000000000..5553962da --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_content_location_resolver_impl.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "lr_location_resolver_impl_base.hpp" + +namespace ams::lr { + + class ContentLocationResolverImpl : public LocationResolverImplBase { + private: + ncm::StorageId storage_id; + + /* Objects for this storage type. */ + ncm::ContentMetaDatabase content_meta_database; + ncm::ContentStorage content_storage; + public: + ContentLocationResolverImpl(ncm::StorageId storage_id) : storage_id(storage_id) { /* ... */ } + + ~ContentLocationResolverImpl(); + private: + /* Helper functions. */ + void GetContentStoragePath(Path *out, ncm::ContentId content_id); + public: + /* Actual commands. */ + virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) override; + virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override; + virtual Result ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) override; + virtual Result ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) override; + virtual Result ResolveDataPath(sf::Out out, ncm::DataId id) override; + virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) override; + virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result Refresh() override; + virtual Result RedirectApplicationProgramPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result ClearApplicationRedirectionDeprecated() override; + virtual Result ClearApplicationRedirection(const sf::InArray &excluding_ids) override; + virtual Result EraseProgramRedirection(ncm::ProgramId id) override; + virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override; + virtual Result EraseApplicationHtmlDocumentRedirection(ncm::ProgramId id) override; + virtual Result EraseApplicationLegalInformationRedirection(ncm::ProgramId id) override; + virtual Result ResolveProgramPathForDebug(sf::Out out, ncm::ProgramId id) override; + virtual Result RedirectProgramPathForDebug(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationProgramPathForDebugDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationProgramPathForDebug(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result EraseProgramRedirectionForDebug(ncm::ProgramId id) override; + }; + +} diff --git a/libraries/libstratosphere/source/lr/lr_location_redirector.cpp b/libraries/libstratosphere/source/lr/lr_location_redirector.cpp new file mode 100644 index 000000000..9c0445dc5 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_location_redirector.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "lr_location_redirector.hpp" + +namespace ams::lr { + + class LocationRedirector::Redirection : public util::IntrusiveListBaseNode { + NON_COPYABLE(Redirection); + NON_MOVEABLE(Redirection); + private: + ncm::ProgramId program_id; + ncm::ProgramId owner_id; + Path path; + u32 flags; + public: + Redirection(ncm::ProgramId program_id, ncm::ProgramId owner_id, const Path& path, u32 flags) : + program_id(program_id), owner_id(owner_id), path(path), flags(flags) { /* ... */ } + + ncm::ProgramId GetProgramId() const { + return this->program_id; + } + + ncm::ProgramId GetOwnerProgramId() const { + return this->owner_id; + } + + void GetPath(Path *out) const { + *out = this->path; + } + + u32 GetFlags() const { + return this->flags; + } + + void SetFlags(u32 flags) { + this->flags = flags; + } + }; + + bool LocationRedirector::FindRedirection(Path *out, ncm::ProgramId program_id) const { + /* Obtain the path of a matching redirection. */ + for (const auto &redirection : this->redirection_list) { + if (redirection.GetProgramId() == program_id) { + redirection.GetPath(out); + return true; + } + } + return false; + } + + void LocationRedirector::SetRedirection(ncm::ProgramId program_id, const Path &path, u32 flags) { + this->SetRedirection(program_id, ncm::InvalidProgramId, path, flags); + } + + void LocationRedirector::SetRedirection(ncm::ProgramId program_id, ncm::ProgramId owner_id, const Path &path, u32 flags) { + /* Remove any existing redirections for this program id. */ + this->EraseRedirection(program_id); + + /* Insert a new redirection into the list. */ + this->redirection_list.push_back(*(new Redirection(program_id, owner_id, path, flags))); + } + + void LocationRedirector::SetRedirectionFlags(ncm::ProgramId program_id, u32 flags) { + /* Set the flags of a redirection with a matching program id. */ + for (auto &redirection : this->redirection_list) { + if (redirection.GetProgramId() == program_id) { + redirection.SetFlags(flags); + break; + } + } + } + + void LocationRedirector::EraseRedirection(ncm::ProgramId program_id) + { + /* Remove any redirections with a matching program id. */ + for (auto &redirection : this->redirection_list) { + if (redirection.GetProgramId() == program_id) { + this->redirection_list.erase(this->redirection_list.iterator_to(redirection)); + delete &redirection; + break; + } + } + } + + void LocationRedirector::ClearRedirections(u32 flags) { + /* Remove any redirections with matching flags. */ + for (auto it = this->redirection_list.begin(); it != this->redirection_list.end();) { + if ((it->GetFlags() & flags) == flags) { + auto old = it; + it = this->redirection_list.erase(it); + delete std::addressof(*old); + } else { + it++; + } + } + } + + void LocationRedirector::ClearRedirectionsExcludingOwners(const ncm::ProgramId *excluding_ids, size_t num_ids) { + for (auto it = this->redirection_list.begin(); it != this->redirection_list.end();) { + /* Skip removal if the redirection has an excluded owner program id. */ + if (this->IsExcluded(it->GetOwnerProgramId(), excluding_ids, num_ids)) { + it++; + continue; + } + + /* Remove the redirection. */ + auto old = it; + it = this->redirection_list.erase(it); + delete std::addressof(*old); + } + } + +} diff --git a/libraries/libstratosphere/source/lr/lr_location_redirector.hpp b/libraries/libstratosphere/source/lr/lr_location_redirector.hpp new file mode 100644 index 000000000..766ce10c6 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_location_redirector.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::lr { + + enum RedirectionFlags { + RedirectionFlags_None = (0 << 0), + RedirectionFlags_Application = (1 << 0), + }; + + class LocationRedirector { + NON_COPYABLE(LocationRedirector); + NON_MOVEABLE(LocationRedirector); + private: + class Redirection; + private: + using RedirectionList = ams::util::IntrusiveListBaseTraits::ListType; + private: + RedirectionList redirection_list; + public: + LocationRedirector() { /* ... */ } + ~LocationRedirector() { this->ClearRedirections(); } + + /* API. */ + bool FindRedirection(Path *out, ncm::ProgramId program_id) const; + void SetRedirection(ncm::ProgramId program_id, const Path &path, u32 flags = RedirectionFlags_None); + void SetRedirection(ncm::ProgramId program_id, ncm::ProgramId owner_id, const Path &path, u32 flags = RedirectionFlags_None); + void SetRedirectionFlags(ncm::ProgramId program_id, u32 flags); + void EraseRedirection(ncm::ProgramId program_id); + void ClearRedirections(u32 flags = RedirectionFlags_None); + void ClearRedirectionsExcludingOwners(const ncm::ProgramId *excluding_ids, size_t num_ids); + private: + inline bool IsExcluded(const ncm::ProgramId id, const ncm::ProgramId *excluding_ids, size_t num_ids) const { + for (size_t i = 0; i < num_ids; i++) { + if (id == excluding_ids[i]) { + return true; + } + } + + return false; + } + }; + +} diff --git a/libraries/libstratosphere/source/lr/lr_location_resolver_impl_base.hpp b/libraries/libstratosphere/source/lr/lr_location_resolver_impl_base.hpp new file mode 100644 index 000000000..d8931d041 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_location_resolver_impl_base.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include "lr_location_redirector.hpp" + +namespace ams::lr { + + class LocationResolverImplBase : public ILocationResolver { + NON_COPYABLE(LocationResolverImplBase); + NON_MOVEABLE(LocationResolverImplBase); + protected: + /* Location redirectors. */ + LocationRedirector program_redirector; + LocationRedirector debug_program_redirector; + LocationRedirector app_control_redirector; + LocationRedirector html_docs_redirector; + LocationRedirector legal_info_redirector; + protected: + LocationResolverImplBase() : program_redirector(), debug_program_redirector(), app_control_redirector(), html_docs_redirector(), legal_info_redirector() { /* ... */ } + protected: + /* Helper functions. */ + void ClearRedirections(u32 flags = RedirectionFlags_None) { + this->program_redirector.ClearRedirections(flags); + this->debug_program_redirector.ClearRedirections(flags); + this->app_control_redirector.ClearRedirections(flags); + this->html_docs_redirector.ClearRedirections(flags); + this->legal_info_redirector.ClearRedirections(flags); + } + + void ClearRedirections(const ncm::ProgramId *excluding_ids, size_t num_ids) { + this->program_redirector.ClearRedirectionsExcludingOwners(excluding_ids, num_ids); + this->debug_program_redirector.ClearRedirectionsExcludingOwners(excluding_ids, num_ids); + this->app_control_redirector.ClearRedirectionsExcludingOwners(excluding_ids, num_ids); + this->html_docs_redirector.ClearRedirectionsExcludingOwners(excluding_ids, num_ids); + this->legal_info_redirector.ClearRedirectionsExcludingOwners(excluding_ids, num_ids); + } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/lr/lr_location_resolver_manager_impl.cpp b/libraries/libstratosphere/source/lr/lr_location_resolver_manager_impl.cpp new file mode 100644 index 000000000..f726cdf7e --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_location_resolver_manager_impl.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include "lr_content_location_resolver_impl.hpp" +#include "lr_redirect_only_location_resolver_impl.hpp" +#include "lr_add_on_content_location_resolver_impl.hpp" +#include "lr_registered_location_resolver_impl.hpp" + +namespace ams::lr { + + Result LocationResolverManagerImpl::OpenLocationResolver(sf::Out> out, ncm::StorageId storage_id) { + std::scoped_lock lk(this->mutex); + /* Find an existing resolver. */ + auto resolver = this->location_resolvers.Find(storage_id); + + /* No existing resolver is present, create one. */ + if (!resolver) { + if (storage_id == ncm::StorageId::Host) { + AMS_ABORT_UNLESS(this->location_resolvers.Insert(storage_id, std::make_shared())); + } else { + auto content_resolver = std::make_shared(storage_id); + R_TRY(content_resolver->Refresh()); + AMS_ABORT_UNLESS(this->location_resolvers.Insert(storage_id, std::move(content_resolver))); + } + + /* Acquire the newly-created resolver. */ + resolver = this->location_resolvers.Find(storage_id); + } + + /* Copy the output interface. */ + out.SetValue(std::shared_ptr(*resolver)); + return ResultSuccess(); + } + + Result LocationResolverManagerImpl::OpenRegisteredLocationResolver(sf::Out> out) { + std::scoped_lock lk(this->mutex); + + /* No existing resolver is present, create one. */ + if (!this->registered_location_resolver) { + this->registered_location_resolver = std::make_shared(); + } + + /* Copy the output interface. */ + out.SetValue(std::shared_ptr(this->registered_location_resolver)); + return ResultSuccess(); + } + + Result LocationResolverManagerImpl::RefreshLocationResolver(ncm::StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Attempt to find an existing resolver. */ + auto resolver = this->location_resolvers.Find(storage_id); + R_UNLESS(resolver, lr::ResultUnknownStorageId()); + + /* Refresh the resolver. */ + if (storage_id != ncm::StorageId::Host) { + (*resolver)->Refresh(); + } + + return ResultSuccess(); + } + + Result LocationResolverManagerImpl::OpenAddOnContentLocationResolver(sf::Out> out) { + std::scoped_lock lk(this->mutex); + + /* No existing resolver is present, create one. */ + if (!this->add_on_content_location_resolver) { + this->add_on_content_location_resolver = std::make_shared(); + } + + /* Copy the output interface. */ + out.SetValue(std::shared_ptr(this->add_on_content_location_resolver)); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.cpp b/libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.cpp new file mode 100644 index 000000000..29aa4c06e --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "lr_redirect_only_location_resolver_impl.hpp" + +namespace ams::lr { + + RedirectOnlyLocationResolverImpl::~RedirectOnlyLocationResolverImpl() { + /* Ensure entries are deallocated */ + this->ClearRedirections(); + } + + Result RedirectOnlyLocationResolverImpl::ResolveProgramPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(this->program_redirector.FindRedirection(out.GetPointer(), id), lr::ResultProgramNotFound()); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectProgramPath(const Path &path, ncm::ProgramId id) { + this->program_redirector.SetRedirection(id, path); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(this->app_control_redirector.FindRedirection(out.GetPointer(), id), lr::ResultControlNotFound()); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(this->html_docs_redirector.FindRedirection(out.GetPointer(), id), lr::ResultHtmlDocumentNotFound()); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::ResolveDataPath(sf::Out out, ncm::DataId id) { + return ResultDataNotFound(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) { + this->app_control_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->app_control_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) { + this->html_docs_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->html_docs_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(this->legal_info_redirector.FindRedirection(out.GetPointer(), id), lr::ResultLegalInformationNotFound()); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) { + this->legal_info_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->legal_info_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::Refresh() { + this->ClearRedirections(); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationProgramPathDeprecated(const Path &path, ncm::ProgramId id) { + this->program_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->program_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::ClearApplicationRedirectionDeprecated() { + this->ClearRedirections(RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::ClearApplicationRedirection(const sf::InArray &excluding_ids) { + this->ClearRedirections(excluding_ids.GetPointer(), excluding_ids.GetSize()); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::EraseProgramRedirection(ncm::ProgramId id) { + this->program_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::EraseApplicationControlRedirection(ncm::ProgramId id) { + this->app_control_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::EraseApplicationHtmlDocumentRedirection(ncm::ProgramId id) { + this->html_docs_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::EraseApplicationLegalInformationRedirection(ncm::ProgramId id) { + this->legal_info_redirector.EraseRedirection(id); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::ResolveProgramPathForDebug(sf::Out out, ncm::ProgramId id) { + /* If a debug program redirection is present, use it. */ + R_SUCCEED_IF(this->debug_program_redirector.FindRedirection(out.GetPointer(), id)); + + /* Otherwise, try to find a normal program redirection. */ + R_UNLESS(this->program_redirector.FindRedirection(out.GetPointer(), id), lr::ResultDebugProgramNotFound()); + + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectProgramPathForDebug(const Path &path, ncm::ProgramId id) { + this->debug_program_redirector.SetRedirection(id, path); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationProgramPathForDebugDeprecated(const Path &path, ncm::ProgramId id) { + this->debug_program_redirector.SetRedirection(id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::RedirectApplicationProgramPathForDebug(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->debug_program_redirector.SetRedirection(id, owner_id, path, RedirectionFlags_Application); + return ResultSuccess(); + } + + Result RedirectOnlyLocationResolverImpl::EraseProgramRedirectionForDebug(ncm::ProgramId id) { + this->debug_program_redirector.EraseRedirection(id); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.hpp new file mode 100644 index 000000000..a204169d1 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_redirect_only_location_resolver_impl.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "lr_location_resolver_impl_base.hpp" + +namespace ams::lr { + + class RedirectOnlyLocationResolverImpl : public LocationResolverImplBase { + public: + ~RedirectOnlyLocationResolverImpl(); + public: + /* Actual commands. */ + virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) override; + virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override; + virtual Result ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) override; + virtual Result ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) override; + virtual Result ResolveDataPath(sf::Out out, ncm::DataId id) override; + virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) override; + virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result Refresh() override; + virtual Result RedirectApplicationProgramPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result ClearApplicationRedirectionDeprecated() override; + virtual Result ClearApplicationRedirection(const sf::InArray &excluding_ids) override; + virtual Result EraseProgramRedirection(ncm::ProgramId id) override; + virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override; + virtual Result EraseApplicationHtmlDocumentRedirection(ncm::ProgramId id) override; + virtual Result EraseApplicationLegalInformationRedirection(ncm::ProgramId id) override; + virtual Result ResolveProgramPathForDebug(sf::Out out, ncm::ProgramId id) override; + virtual Result RedirectProgramPathForDebug(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationProgramPathForDebugDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectApplicationProgramPathForDebug(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result EraseProgramRedirectionForDebug(ncm::ProgramId id) override; + }; + +} diff --git a/libraries/libstratosphere/source/lr/lr_registered_data.hpp b/libraries/libstratosphere/source/lr/lr_registered_data.hpp new file mode 100644 index 000000000..109baa5f2 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_registered_data.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::lr { + + template + class RegisteredData { + NON_COPYABLE(RegisteredData); + NON_MOVEABLE(RegisteredData); + private: + struct Entry { + Value value; + ncm::ProgramId owner_id; + Key key; + bool is_valid; + }; + private: + Entry entries[NumEntries]; + size_t capacity; + private: + inline bool IsExcluded(const ncm::ProgramId id, const ncm::ProgramId *excluding_ids, size_t num_ids) const { + /* Try to find program id in exclusions. */ + for (size_t i = 0; i < num_ids; i++) { + if (id == excluding_ids[i]) { + return true; + } + } + + return false; + } + + inline void RegisterImpl(size_t i, const Key &key, const Value &value, const ncm::ProgramId owner_id) { + /* Populate entry. */ + Entry& entry = this->entries[i]; + entry.key = key; + entry.value = value; + entry.owner_id = owner_id; + entry.is_valid = true; + } + public: + RegisteredData(size_t capacity = NumEntries) : capacity(capacity) { + this->Clear(); + } + + bool Register(const Key &key, const Value &value, const ncm::ProgramId owner_id) { + /* Try to find an existing value. */ + for (size_t i = 0; i < this->GetCapacity(); i++) { + Entry& entry = this->entries[i]; + if (entry.is_valid && entry.key == key) { + this->RegisterImpl(i, key, value, owner_id); + return true; + } + } + + /* We didn't find an existing entry, so try to create a new one. */ + for (size_t i = 0; i < this->GetCapacity(); i++) { + Entry& entry = this->entries[i]; + if (!entry.is_valid) { + this->RegisterImpl(i, key, value, owner_id); + return true; + } + } + + return false; + } + + void Unregister(const Key &key) { + /* Invalidate entries with a matching key. */ + for (size_t i = 0; i < this->GetCapacity(); i++) { + Entry& entry = this->entries[i]; + if (entry.is_valid && entry.key == key) { + entry.is_valid = false; + } + } + } + + void UnregisterOwnerProgram(ncm::ProgramId owner_id) { + /* Invalidate entries with a matching owner id. */ + for (size_t i = 0; i < this->GetCapacity(); i++) { + Entry& entry = this->entries[i]; + if (entry.owner_id == owner_id) { + entry.is_valid = false; + } + } + } + + bool Find(Value *out, const Key &key) const { + /* Locate a matching entry. */ + for (size_t i = 0; i < this->GetCapacity(); i++) { + const Entry& entry = this->entries[i]; + if (entry.is_valid && entry.key == key) { + *out = entry.value; + return true; + } + } + + return false; + } + + void Clear() { + /* Invalidate all entries. */ + for (size_t i = 0; i < this->GetCapacity(); i++) { + this->entries[i].is_valid = false; + } + } + + void ClearExcluding(const ncm::ProgramId *ids, size_t num_ids) { + /* Invalidate all entries unless excluded. */ + for (size_t i = 0; i < this->GetCapacity(); i++) { + Entry& entry = this->entries[i]; + + if (!this->IsExcluded(entry.owner_id, ids, num_ids)) { + entry.is_valid = false; + } + } + } + + size_t GetCapacity() const { + return this->capacity; + } + }; + + template + using RegisteredLocations = RegisteredData; + + template + using RegisteredStorages = RegisteredData; + +} diff --git a/libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.cpp b/libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.cpp new file mode 100644 index 000000000..d499c1423 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "lr_registered_location_resolver_impl.hpp" + +namespace ams::lr { + + namespace { + + template + bool ResolvePath(Path *out, const LocationRedirector &redirector, const RegisteredLocations &locations, ncm::ProgramId id) { + /* Attempt to use a redirection if present. */ + if (!redirector.FindRedirection(out, id)) { + /* Otherwise try and use a registered location. */ + if (!locations.Find(out, id)) { + return false; + } + } + return true; + } + + template + void RegisterPath(RegisteredLocations &locations, ncm::ProgramId id, const Path& path, ncm::ProgramId owner_id) { + /* If we register successfully, we're good. */ + if (locations.Register(id, path, owner_id)) { + return; + } + + /* Otherwise, clear and register (this should always succeed). */ + locations.Clear(); + locations.Register(id, path, owner_id); + } + + } + + RegisteredLocationResolverImpl::~RegisteredLocationResolverImpl() { + /* Ensure entries are deallocated */ + this->ClearRedirections(); + } + + /* Helper function. */ + void RegisteredLocationResolverImpl::ClearRedirections(u32 flags) { + this->html_docs_redirector.ClearRedirections(flags); + this->program_redirector.ClearRedirections(flags); + } + + Result RegisteredLocationResolverImpl::RefreshImpl(const ncm::ProgramId *excluding_ids, size_t num_ids) { + /* On < 9.0.0, exclusion lists were not supported yet, so simply clear and return. */ + if (hos::GetVersion() < hos::Version_900) { + this->ClearRedirections(); + return ResultSuccess(); + } + + if (num_ids) { + /* If we have exclusion lists, explicitly clear our locations. */ + this->registered_program_locations.ClearExcluding(excluding_ids, num_ids); + this->registered_html_docs_locations.ClearExcluding(excluding_ids, num_ids); + } else { + /* If we don't, just perform a general clear (as pre 9.0.0 did). */ + this->ClearRedirections(); + } + + /* Clear redirectors using exclusion lists. */ + this->program_redirector.ClearRedirectionsExcludingOwners(excluding_ids, num_ids); + this->html_docs_redirector.ClearRedirectionsExcludingOwners(excluding_ids, num_ids); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::ResolveProgramPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(ResolvePath(out.GetPointer(), this->program_redirector, this->registered_program_locations, id), lr::ResultProgramNotFound()); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RegisterProgramPathDeprecated(const Path &path, ncm::ProgramId id) { + RegisterPath(this->registered_program_locations, id, path, ncm::InvalidProgramId); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RegisterProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + RegisterPath(this->registered_program_locations, id, path, owner_id); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::UnregisterProgramPath(ncm::ProgramId id) { + this->registered_program_locations.Unregister(id); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RedirectProgramPathDeprecated(const Path &path, ncm::ProgramId id) { + this->program_redirector.SetRedirection(id, path); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RedirectProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->program_redirector.SetRedirection(id, owner_id, path); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::ResolveHtmlDocumentPath(sf::Out out, ncm::ProgramId id) { + R_UNLESS(ResolvePath(out.GetPointer(), this->html_docs_redirector, this->registered_html_docs_locations, id), lr::ResultHtmlDocumentNotFound()); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RegisterHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) { + RegisterPath(this->registered_html_docs_locations, id, path, ncm::InvalidProgramId); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RegisterHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + RegisterPath(this->registered_html_docs_locations, id, path, owner_id); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::UnregisterHtmlDocumentPath(ncm::ProgramId id) { + this->registered_html_docs_locations.Unregister(id); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RedirectHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) { + this->html_docs_redirector.SetRedirection(id, path); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::RedirectHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) { + this->html_docs_redirector.SetRedirection(id, owner_id, path); + return ResultSuccess(); + } + + Result RegisteredLocationResolverImpl::Refresh() { + return this->RefreshImpl(nullptr, 0); + } + + Result RegisteredLocationResolverImpl::RefreshExcluding(const sf::InArray &ids) { + return this->RefreshImpl(ids.GetPointer(), ids.GetSize()); + } + +} diff --git a/libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.hpp new file mode 100644 index 000000000..a5d9837ee --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_registered_location_resolver_impl.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include "lr_location_redirector.hpp" +#include "lr_registered_data.hpp" + +namespace ams::lr { + + class RegisteredLocationResolverImpl : public IRegisteredLocationResolver { + private: + static constexpr size_t MaxRegisteredLocationsDeprecated = 0x10; + static constexpr size_t MaxRegisteredLocations = 0x20; + static_assert(MaxRegisteredLocations >= MaxRegisteredLocationsDeprecated); + private: + static ALWAYS_INLINE size_t GetMaxRegisteredLocations() { + if (hos::GetVersion() >= hos::Version_900) { + return MaxRegisteredLocations; + } else { + return MaxRegisteredLocationsDeprecated; + } + } + private: + /* Redirection and registered location storage. */ + LocationRedirector program_redirector; + RegisteredLocations registered_program_locations; + LocationRedirector html_docs_redirector; + RegisteredLocations registered_html_docs_locations; + private: + /* Helper functions. */ + void ClearRedirections(u32 flags = RedirectionFlags_None); + Result RefreshImpl(const ncm::ProgramId *excluding_ids, size_t num_ids); + public: + RegisteredLocationResolverImpl() : registered_program_locations(GetMaxRegisteredLocations()), registered_html_docs_locations(GetMaxRegisteredLocations()) { /* ... */ } + ~RegisteredLocationResolverImpl(); + public: + /* Actual commands. */ + virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) override; + virtual Result RegisterProgramPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RegisterProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result UnregisterProgramPath(ncm::ProgramId id) override; + virtual Result RedirectProgramPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result ResolveHtmlDocumentPath(sf::Out out, ncm::ProgramId id) override; + virtual Result RegisterHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RegisterHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result UnregisterHtmlDocumentPath(ncm::ProgramId id) override; + virtual Result RedirectHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override; + virtual Result RedirectHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override; + virtual Result Refresh() override; + virtual Result RefreshExcluding(const sf::InArray &ids) override; + }; + +} diff --git a/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp new file mode 100644 index 000000000..5ac2b47e4 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::lr { + + class RemoteLocationResolverImpl : public ILocationResolver { + private: + ::LrLocationResolver srv; + public: + RemoteLocationResolverImpl(::LrLocationResolver &l) : srv(l) { /* ... */ } + + ~RemoteLocationResolverImpl() { ::serviceClose(&srv.s); } + public: + /* Actual commands. */ + virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) override { + return lrLrResolveProgramPath(std::addressof(this->srv), static_cast(id), out->str); + } + + virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override { + return lrLrRedirectProgramPath(std::addressof(this->srv), static_cast(id), path.str); + } + + virtual Result ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) override { + return lrLrResolveApplicationControlPath(std::addressof(this->srv), static_cast(id), out->str); + } + + virtual Result ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) override { + return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), out->str); + } + + virtual Result ResolveDataPath(sf::Out out, ncm::DataId id) override { + return lrLrResolveDataPath(std::addressof(this->srv), id.value, out->str); + } + + virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override { + return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast(id), 0, path.str); + } + + virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + } + + virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override { + return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), 0, path.str); + } + + virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + } + + virtual Result ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) override { + return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), out->str); + } + + virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override { + return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), 0, path.str); + } + + virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + } + + virtual Result Refresh() override { + return lrLrRefresh(std::addressof(this->srv)); + } + + virtual Result RedirectApplicationProgramPathDeprecated(const Path &path, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectApplicationProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result ClearApplicationRedirectionDeprecated() override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result ClearApplicationRedirection(const sf::InArray &excluding_ids) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result EraseProgramRedirection(ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result EraseApplicationHtmlDocumentRedirection(ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result EraseApplicationLegalInformationRedirection(ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result ResolveProgramPathForDebug(sf::Out out, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectProgramPathForDebug(const Path &path, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectApplicationProgramPathForDebugDeprecated(const Path &path, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectApplicationProgramPathForDebug(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result EraseProgramRedirectionForDebug(ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + }; + +} diff --git a/libraries/libstratosphere/source/lr/lr_remote_registered_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_remote_registered_location_resolver_impl.hpp new file mode 100644 index 000000000..3b8551756 --- /dev/null +++ b/libraries/libstratosphere/source/lr/lr_remote_registered_location_resolver_impl.hpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::lr { + + class RemoteRegisteredLocationResolverImpl : public IRegisteredLocationResolver { + private: + ::LrRegisteredLocationResolver srv; + public: + RemoteRegisteredLocationResolverImpl(::LrRegisteredLocationResolver &l) : srv(l) { /* ... */ } + + ~RemoteRegisteredLocationResolverImpl() { ::serviceClose(&srv.s); } + public: + /* Actual commands. */ + virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) override { + return lrRegLrResolveProgramPath(std::addressof(this->srv), static_cast(id), out->str); + } + + virtual Result RegisterProgramPathDeprecated(const Path &path, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RegisterProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result UnregisterProgramPath(ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectProgramPathDeprecated(const Path &path, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result ResolveHtmlDocumentPath(sf::Out out, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RegisterHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RegisterHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result UnregisterHtmlDocumentPath(ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RedirectHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result Refresh() override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result RefreshExcluding(const sf::InArray &ids) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_api.cpp b/libraries/libstratosphere/source/ncm/ncm_api.cpp new file mode 100644 index 000000000..7dbed826c --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_api.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_remote_content_manager_impl.hpp" + +namespace ams::ncm { + + namespace { + + std::shared_ptr g_content_manager; + + } + + void Initialize() { + AMS_ASSERT(g_content_manager == nullptr); + R_ABORT_UNLESS(ncmInitialize()); + g_content_manager = std::make_shared(); + } + + void Finalize() { + AMS_ASSERT(g_content_manager != nullptr); + g_content_manager.reset(); + ncmExit(); + } + + void InitializeWithObject(std::shared_ptr manager_object) { + AMS_ASSERT(g_content_manager == nullptr); + g_content_manager = manager_object; + AMS_ASSERT(g_content_manager != nullptr); + } + + /* Service API. */ + Result CreateContentStorage(StorageId storage_id) { + return g_content_manager->CreateContentStorage(storage_id); + } + + Result CreateContentMetaDatabase(StorageId storage_id) { + return g_content_manager->CreateContentMetaDatabase(storage_id); + } + + Result VerifyContentStorage(StorageId storage_id) { + return g_content_manager->VerifyContentStorage(storage_id); + } + + Result VerifyContentMetaDatabase(StorageId storage_id) { + return g_content_manager->VerifyContentMetaDatabase(storage_id); + } + + Result OpenContentStorage(ContentStorage *out, StorageId storage_id) { + sf::cmif::ServiceObjectHolder object_holder; + R_TRY(g_content_manager->OpenContentStorage(std::addressof(object_holder), storage_id)); + + *out = ContentStorage(object_holder.GetServiceObject()); + return ResultSuccess(); + } + + Result OpenContentMetaDatabase(ContentMetaDatabase *out, StorageId storage_id) { + sf::cmif::ServiceObjectHolder object_holder; + R_TRY(g_content_manager->OpenContentMetaDatabase(std::addressof(object_holder), storage_id)); + + *out = ContentMetaDatabase(object_holder.GetServiceObject()); + return ResultSuccess(); + } + + Result CleanupContentMetaDatabase(StorageId storage_id) { + return g_content_manager->CleanupContentMetaDatabase(storage_id); + } + + Result ActivateContentStorage(StorageId storage_id) { + return g_content_manager->ActivateContentStorage(storage_id); + } + + Result InactivateContentStorage(StorageId storage_id) { + return g_content_manager->InactivateContentStorage(storage_id); + } + + Result ActivateContentMetaDatabase(StorageId storage_id) { + return g_content_manager->ActivateContentMetaDatabase(storage_id); + } + + Result InactivateContentMetaDatabase(StorageId storage_id) { + return g_content_manager->InactivateContentMetaDatabase(storage_id); + } + + Result InvalidateRightsIdCache() { + return g_content_manager->InvalidateRightsIdCache(); + } + + /* Deprecated API. */ + Result CloseContentStorageForcibly(StorageId storage_id) { + AMS_ABORT_UNLESS(hos::GetVersion() == hos::Version_100); + return g_content_manager->CloseContentStorageForcibly(storage_id); + } + + Result CloseContentMetaDatabaseForcibly(StorageId storage_id) { + AMS_ABORT_UNLESS(hos::GetVersion() == hos::Version_100); + return g_content_manager->CloseContentMetaDatabaseForcibly(storage_id); + } +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_id_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_id_utils.cpp new file mode 100644 index 000000000..4450693c3 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_id_utils.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + namespace { + + void GetStringFromBytes(char *dst, const void *src, size_t count) { + for (size_t i = 0; i < count; i++) { + std::snprintf(dst + 2 * i, 3, "%02x", static_cast(src)[i]); + } + } + + bool GetBytesFromString(void *dst, size_t dst_size, const char *src, size_t src_size) { + /* Each byte is comprised of hex characters. */ + if (!util::IsAligned(src_size, 2) || (dst_size * 2 < src_size)) { + return false; + } + + /* Convert each character pair to a byte until we reach the end. */ + for (size_t i = 0; i < src_size; i += 2) { + char tmp[3]; + strlcpy(tmp, src + i, sizeof(tmp)); + + char *err = nullptr; + reinterpret_cast(dst)[i / 2] = static_cast(std::strtoul(tmp, std::addressof(err), 16)); + if (*err != '\x00') { + return false; + } + } + + return true; + } + + } + + + ContentIdString GetContentIdString(ContentId id) { + ContentIdString str; + GetStringFromContentId(str.data, sizeof(str), id); + return str; + } + + void GetStringFromContentId(char *dst, size_t dst_size, ContentId id) { + AMS_ABORT_UNLESS(dst_size > ContentIdStringLength); + GetStringFromBytes(dst, std::addressof(id), sizeof(id)); + } + + std::optional GetContentIdFromString(const char *str, size_t len) { + if (len < ContentIdStringLength) { + return std::nullopt; + } + + ContentId content_id; + return GetBytesFromString(std::addressof(content_id), sizeof(content_id), str, ContentIdStringLength) ? std::optional(content_id) : std::nullopt; + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp new file mode 100644 index 000000000..291237088 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_fs_utils.hpp" + +namespace ams::ncm { + + namespace { + + constexpr inline size_t MaxPackagePathLength = 0x100; + + Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *package_root_path, const char *entry_path) { + char package_path[MaxPackagePathLength]; + + const size_t path_len = std::snprintf(package_path, sizeof(package_path), "%s%s", package_root_path, entry_path); + AMS_ABORT_UNLESS(path_len < MaxPackagePathLength); + + return fs::ConvertToFsCommonPath(dst, dst_size, package_path); + } + + Result LoadContentMeta(ncm::AutoBuffer *out, const char *package_root_path, const fs::DirectoryEntry &entry) { + AMS_ABORT_UNLESS(impl::PathView(entry.name).HasSuffix(".cnmt.nca")); + + char path[MaxPackagePathLength]; + R_TRY(ConvertToFsCommonPath(path, sizeof(path), package_root_path, entry.name)); + + return ncm::ReadContentMetaPath(out, path); + } + + template + Result ForEachFileInDirectory(const char *root_path, F f) { + /* Open the directory. */ + fs::DirectoryHandle dir; + R_TRY(fs::OpenDirectory(std::addressof(dir), root_path, fs::OpenDirectoryMode_File)); + ON_SCOPE_EXIT { fs::CloseDirectory(dir); }; + + while (true) { + /* Read the current entry. */ + s64 count; + fs::DirectoryEntry entry; + R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1)); + if (count == 0) { + break; + } + + /* Invoke our handler on the entry. */ + bool done; + R_TRY(f(std::addressof(done), entry)); + R_SUCCEED_IF(done); + } + + return ResultSuccess(); + } + + } + + Result ContentMetaDatabaseBuilder::BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info) { + /* Create a reader for the content meta. */ + ncm::PackagedContentMetaReader package_meta_reader(buf, size); + + /* Allocate space to hold the converted meta. */ + const size_t meta_size = package_meta_reader.CalculateConvertContentMetaSize(); + void *meta = std::malloc(meta_size); + ON_SCOPE_EXIT { std::free(meta); }; + + /* Convert the meta from packaged form to normal form. */ + package_meta_reader.ConvertToContentMeta(meta, meta_size, meta_info); + ncm::ContentMetaReader meta_reader(meta, meta_size); + + /* Insert the new metas into the database. */ + R_TRY(this->db->Set(package_meta_reader.GetKey(), meta_reader.GetData(), meta_reader.GetSize())); + + /* We're done. */ + return ResultSuccess(); + } + + Result ContentMetaDatabaseBuilder::BuildFromStorage(ContentStorage *storage) { + /* Get the total count of contents. */ + s32 total_count; + R_TRY(storage->GetContentCount(std::addressof(total_count))); + + /* Loop over all contents, looking for a package we can build from. */ + const size_t MaxContentIds = 64; + ContentId content_ids[MaxContentIds]; + for (s32 offset = 0; offset < total_count; /* ... */) { + /* List contents at the current offset. */ + s32 count; + R_TRY(storage->ListContentId(std::addressof(count), content_ids, MaxContentIds, offset)); + + /* Loop the contents we listed, looking for a correct one. */ + for (s32 i = 0; i < count; i++) { + /* Get the path for this content id. */ + auto &content_id = content_ids[i]; + ncm::Path path; + storage->GetPath(std::addressof(path), content_id); + + /* Read the content meta path, and build. */ + ncm::AutoBuffer package_meta; + if (R_SUCCEEDED(ncm::ReadContentMetaPath(std::addressof(package_meta), path.str))) { + /* Get the size of the content. */ + s64 size; + R_TRY(storage->GetSize(std::addressof(size), content_id)); + + /* Build. */ + R_TRY(this->BuildFromPackageContentMeta(package_meta.Get(), package_meta.GetSize(), ContentInfo::Make(content_id, size, ContentType::Meta))); + } + } + + /* Advance. */ + offset += count; + } + + /* Commit our changes. */ + return this->db->Commit(); + } + + Result ContentMetaDatabaseBuilder::BuildFromPackage(const char *package_root_path) { + /* Build the database by writing every entry in the package. */ + R_TRY(ForEachFileInDirectory(package_root_path, [&](bool *done, const fs::DirectoryEntry &entry) -> Result { + /* Never early terminate. */ + *done = false; + + /* We have nothing to list if we're not looking at a meta. */ + R_SUCCEED_IF(!impl::PathView(entry.name).HasSuffix(".cnmt.nca")); + + /* Read the content meta path, and build. */ + ncm::AutoBuffer package_meta; + R_TRY(LoadContentMeta(std::addressof(package_meta), package_root_path, entry)); + + /* Try to parse a content id from the name. */ + auto content_id = GetContentIdFromString(entry.name, sizeof(entry.name)); + R_UNLESS(content_id, ncm::ResultInvalidPackageFormat()); + + /* Build using the meta. */ + return this->BuildFromPackageContentMeta(package_meta.Get(), package_meta.GetSize(), ContentInfo::Make(*content_id, entry.file_size, ContentType::Meta)); + })); + + /* Commit our changes. */ + return this->db->Commit(); + } + + Result ContentMetaDatabaseBuilder::Cleanup() { + /* This cleans up the content meta by removing all entries. */ + while (true) { + /* List as many keys as we can. */ + constexpr s32 MaxKeys = 64; + ContentMetaKey keys[MaxKeys]; + auto list_count = this->db->ListContentMeta(keys, MaxKeys); + + /* Remove the listed keys. */ + for (auto i = 0; i < list_count.written; i++) { + R_TRY(this->db->Remove(keys[i])); + } + + /* If there aren't more keys to read, we're done. */ + if (list_count.written < MaxKeys) { + break; + } + } + + /* Commit our deletions. */ + return this->db->Commit(); + } + + Result ListApplicationPackage(s32 *out_count, ApplicationId *out_ids, size_t max_out_ids, const char *package_root_path) { + size_t count = 0; + R_TRY(ForEachFileInDirectory(package_root_path, [&](bool *done, const fs::DirectoryEntry &entry) -> Result { + /* Never early terminate. */ + *done = false; + + /* We have nothing to list if we're not looking at a meta. */ + R_SUCCEED_IF(!impl::PathView(entry.name).HasSuffix(".cnmt.nca")); + + /* Read the content meta path, and build. */ + ncm::AutoBuffer package_meta; + R_TRY(LoadContentMeta(std::addressof(package_meta), package_root_path, entry)); + + /* Create a reader for the meta. */ + ncm::PackagedContentMetaReader package_meta_reader(package_meta.Get(), package_meta.GetSize()); + + /* Write the key to output if we're reading an application. */ + const auto &key = package_meta_reader.GetKey(); + if (key.type == ContentMetaType::Application) { + R_UNLESS(count < max_out_ids, ncm::ResultBufferInsufficient()); + + out_ids[count++] = { key.id }; + } + + return ResultSuccess(); + })); + + *out_count = static_cast(count); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp new file mode 100644 index 000000000..d9d35d7a7 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_content_storage_impl.hpp" +#include "ncm_read_only_content_storage_impl.hpp" +#include "ncm_content_meta_database_impl.hpp" +#include "ncm_on_memory_content_meta_database_impl.hpp" +#include "ncm_fs_utils.hpp" + +namespace ams::ncm { + + namespace { + + constexpr fs::SystemSaveDataId BuiltInSystemSaveDataId = 0x8000000000000120; + constexpr u64 BuiltInSystemSaveDataSize = 0x6c000; + constexpr u64 BuiltInSystemSaveDataJournalSize = 0x6c000; + constexpr u32 BuiltInSystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveData | fs::SaveDataFlags_KeepAfterRefurbishment; + + constexpr SystemSaveDataInfo BuiltInSystemSystemSaveDataInfo = { + .id = BuiltInSystemSaveDataId, + .size = BuiltInSystemSaveDataSize, + .journal_size = BuiltInSystemSaveDataJournalSize, + .flags = BuiltInSystemSaveDataFlags, + .space_id = fs::SaveDataSpaceId::System + }; + + constexpr fs::SystemSaveDataId BuiltInUserSaveDataId = 0x8000000000000121; + constexpr u64 BuiltInUserSaveDataSize = 0x29e000; + constexpr u64 BuiltInUserSaveDataJournalSize = 0x29e000; + constexpr u32 BuiltInUserSaveDataFlags = 0; + + constexpr SystemSaveDataInfo BuiltInUserSystemSaveDataInfo = { + .id = BuiltInUserSaveDataId, + .size = BuiltInUserSaveDataSize, + .journal_size = BuiltInUserSaveDataJournalSize, + .flags = BuiltInUserSaveDataFlags, + .space_id = fs::SaveDataSpaceId::System + }; + + constexpr fs::SystemSaveDataId SdCardSaveDataId = 0x8000000000000124; + constexpr u64 SdCardSaveDataSize = 0xa08000; + constexpr u64 SdCardSaveDataJournalSize = 0xa08000; + constexpr u32 SdCardSaveDataFlags = 0; + + constexpr SystemSaveDataInfo SdCardSystemSaveDataInfo = { + .id = SdCardSaveDataId, + .size = SdCardSaveDataSize, + .journal_size = SdCardSaveDataJournalSize, + .flags = SdCardSaveDataFlags, + .space_id = fs::SaveDataSpaceId::SdSystem, + }; + + constexpr size_t MaxBuiltInSystemContentMetaCount = 0x800; + constexpr size_t MaxBuiltInUserContentMetaCount = 0x2000; + constexpr size_t MaxSdCardContentMetaCount = 0x2000; + constexpr size_t MaxGameCardContentMetaCount = 0x800; + + using RootPath = kvdb::BoundedString<32>; + + inline void ReplaceMountName(char *out_path, const char *mount_name, const char *path) { + std::strcpy(out_path, mount_name); + std::strcat(out_path, std::strchr(path, ':')); + } + + Result EnsureBuiltInSystemSaveDataFlags() { + u32 cur_flags = 0; + + /* Obtain the existing flags. */ + R_TRY(fs::GetSaveDataFlags(std::addressof(cur_flags), BuiltInSystemSaveDataId)); + + /* Update the flags if needed. */ + if (cur_flags != BuiltInSystemSaveDataFlags) { + R_TRY(fs::SetSaveDataFlags(BuiltInSystemSaveDataId, fs::SaveDataSpaceId::System, BuiltInSystemSaveDataFlags)); + } + return ResultSuccess(); + } + + ALWAYS_INLINE Result GetContentStorageNotActiveResult(StorageId storage_id) { + switch (storage_id) { + case StorageId::GameCard: return ResultGameCardContentStorageNotActive(); + case StorageId::BuiltInSystem: return ResultBuiltInSystemContentStorageNotActive(); + case StorageId::BuiltInUser: return ResultBuiltInUserContentStorageNotActive(); + case StorageId::SdCard: return ResultSdCardContentStorageNotActive(); + default: return ResultUnknownContentStorageNotActive(); + } + } + + ALWAYS_INLINE Result GetContentMetaDatabaseNotActiveResult(StorageId storage_id) { + switch (storage_id) { + case StorageId::GameCard: return ResultGameCardContentMetaDatabaseNotActive(); + case StorageId::BuiltInSystem: return ResultBuiltInSystemContentMetaDatabaseNotActive(); + case StorageId::BuiltInUser: return ResultBuiltInUserContentMetaDatabaseNotActive(); + case StorageId::SdCard: return ResultSdCardContentMetaDatabaseNotActive(); + default: return ResultUnknownContentMetaDatabaseNotActive(); + } + } + + ALWAYS_INLINE bool IsSignedSystemPartitionOnSdCardValid(const char *bis_mount_name) { + /* Signed system partition should never be checked on < 4.0.0, as it did not exist before then. */ + AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_400); + + /* If we're importing from system on SD, make sure that the signed system partition is valid. */ + const auto version = hos::GetVersion(); + if (version >= hos::Version_800) { + /* On >= 8.0.0, a simpler method was added to check validity. */ + /* This also works on < 4.0.0 (though the system partition will never be on-sd there), */ + /* and so this will always return false. */ + char path[fs::MountNameLengthMax + 2 /* :/ */ + 1]; + std::snprintf(path, sizeof(path), "%s:/", bis_mount_name); + return fs::IsSignedSystemPartitionOnSdCardValid(path); + } else { + /* On 4.0.0-7.0.1, use the remote command to validate the system partition. */ + return fs::IsSignedSystemPartitionOnSdCardValidDeprecated(); + } + } + } + + ContentManagerImpl::~ContentManagerImpl() { + std::scoped_lock lk(this->mutex); + + /* Disable and unmount all content storage roots. */ + for (auto &root : this->content_storage_roots) { + this->InactivateContentStorage(root.storage_id); + } + + /* Disable and unmount all content meta database roots. */ + for (auto &root : this->content_meta_database_roots) { + this->InactivateContentMetaDatabase(root.storage_id); + } + } + + Result ContentManagerImpl::EnsureAndMountSystemSaveData(const char *mount_name, const SystemSaveDataInfo &info) const { + constexpr u64 OwnerId = 0; + + /* Don't create save if absent - We want to handle this case ourselves. */ + fs::DisableAutoSaveDataCreation(); + + /* Mount existing system save data if present, otherwise create it then mount. */ + R_TRY_CATCH(fs::MountSystemSaveData(mount_name, info.space_id, info.id)) { + R_CATCH(fs::ResultTargetNotFound) { + /* On 1.0.0, not all flags existed. Mask when appropriate. */ + constexpr u32 SaveDataFlags100Mask = fs::SaveDataFlags_KeepAfterResettingSystemSaveData; + const u32 flags = (hos::GetVersion() >= hos::Version_200) ? (info.flags) : (info.flags & SaveDataFlags100Mask); + R_TRY(fs::CreateSystemSaveData(info.space_id, info.id, OwnerId, info.size, info.journal_size, flags)); + R_TRY(fs::MountSystemSaveData(mount_name, info.space_id, info.id)); + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result ContentManagerImpl::GetContentStorageRoot(ContentStorageRoot **out, StorageId id) { + /* Storage must not be StorageId::Any or StorageId::None. */ + R_UNLESS(IsUniqueStorage(id), ncm::ResultUnknownStorage()); + + /* Find a root with a matching storage id. */ + for (auto &root : this->content_storage_roots) { + if (root.storage_id == id) { + *out = std::addressof(root); + return ResultSuccess(); + } + } + + return ncm::ResultUnknownStorage(); + } + + Result ContentManagerImpl::GetContentMetaDatabaseRoot(ContentMetaDatabaseRoot **out, StorageId id) { + /* Storage must not be StorageId::Any or StorageId::None. */ + R_UNLESS(IsUniqueStorage(id), ncm::ResultUnknownStorage()); + + /* Find a root with a matching storage id. */ + for (auto &root : this->content_meta_database_roots) { + if (root.storage_id == id) { + *out = std::addressof(root); + return ResultSuccess(); + } + } + + return ncm::ResultUnknownStorage(); + } + + + Result ContentManagerImpl::InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, fs::ContentStorageId content_storage_id) { + out->storage_id = storage_id; + out->content_storage_id = content_storage_id; + out->content_storage = nullptr; + + /* Create a new mount name and copy it to out. */ + std::strcpy(out->mount_name, impl::CreateUniqueMountName().str); + std::snprintf(out->path, sizeof(out->path), "%s:/", out->mount_name); + + return ResultSuccess(); + } + + Result ContentManagerImpl::InitializeGameCardContentStorageRoot(ContentStorageRoot *out) { + out->storage_id = StorageId::GameCard; + out->content_storage = nullptr; + + /* Create a new mount name and copy it to out. */ + std::strcpy(out->mount_name, impl::CreateUniqueMountName().str); + std::snprintf(out->path, sizeof(out->path), "%s:/", out->mount_name); + + return ResultSuccess(); + } + + Result ContentManagerImpl::InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas) { + out->storage_id = storage_id; + out->info = info; + out->max_content_metas = max_content_metas; + out->content_meta_database = nullptr; + out->kvs = std::nullopt; + + /* Create a new mount name and copy it to out. */ + std::strcpy(out->mount_name, impl::CreateUniqueMountName().str); + out->mount_name[0] = '#'; + std::snprintf(out->path, sizeof(out->path), "%s:/meta", out->mount_name); + + return ResultSuccess(); + } + + Result ContentManagerImpl::InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas) { + out->storage_id = StorageId::GameCard; + out->max_content_metas = max_content_metas; + out->content_meta_database = nullptr; + out->kvs = std::nullopt; + + return ResultSuccess(); + } + + Result ContentManagerImpl::ImportContentMetaDatabaseImpl(StorageId storage_id, const char *import_mount_name, const char *path) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content meta database root. */ + ContentMetaDatabaseRoot *root; + R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id)); + + /* Print the savedata path. */ + PathString savedata_db_path; + savedata_db_path.SetFormat("%s/%s", root->path, "imkvdb.arc"); + + /* Print a path for the mounted partition. */ + PathString bis_db_path; + bis_db_path.SetFormat("%s:/%s", import_mount_name, path); + + /* Mount the savedata. */ + R_TRY(fs::MountSystemSaveData(root->mount_name, root->info.space_id, root->info.id)); + ON_SCOPE_EXIT { fs::Unmount(root->mount_name); }; + + /* Ensure the path exists for us to import to. */ + R_TRY(impl::EnsureDirectoryRecursively(root->path)); + + /* Copy the file from bis to our save. */ + R_TRY(impl::CopyFile(savedata_db_path, bis_db_path)); + + /* Commit the import. */ + return fs::CommitSaveData(root->mount_name); + } + + Result ContentManagerImpl::BuildContentMetaDatabase(StorageId storage_id) { + if (hos::GetVersion() <= hos::Version_400) { + /* 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)); + return builder.BuildFromStorage(std::addressof(storage)); + } else { + /* On 5.0.0+, building just performs an import. */ + return this->ImportContentMetaDatabase(storage_id, false); + } + } + + Result ContentManagerImpl::ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition) { + /* Only support importing BuiltInSystem. */ + AMS_ABORT_UNLESS(storage_id == StorageId::BuiltInSystem); + + /* Get a mount name for the system partition. */ + auto bis_mount_name = impl::CreateUniqueMountName(); + + /* Mount the BIS partition that contains the database we're importing. */ + R_TRY(fs::MountBis(bis_mount_name.str, fs::BisPartitionId::System)); + ON_SCOPE_EXIT { fs::Unmount(bis_mount_name.str); }; + + /* If we're not importing from a signed partition (or the partition signature is valid), import. */ + if (!from_signed_partition || IsSignedSystemPartitionOnSdCardValid(bis_mount_name.str)) { + R_TRY(this->ImportContentMetaDatabaseImpl(StorageId::BuiltInSystem, bis_mount_name.str, "cnmtdb.arc")); + } + + return ResultSuccess(); + } + + Result ContentManagerImpl::Initialize(const ContentManagerConfig &config) { + std::scoped_lock lk(this->mutex); + + /* Check if we've already initialized. */ + R_SUCCEED_IF(this->initialized); + + /* Clear storage id for all roots. */ + for (auto &root : this->content_storage_roots) { + root.storage_id = StorageId::None; + } + + for (auto &root : this->content_meta_database_roots) { + root.storage_id = StorageId::None; + } + + /* First, setup the BuiltInSystem storage entry. */ + R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[this->num_content_storage_entries++], StorageId::BuiltInSystem, fs::ContentStorageId::System)); + if (R_FAILED(this->VerifyContentStorage(StorageId::BuiltInSystem))) { + R_TRY(this->CreateContentStorage(StorageId::BuiltInSystem)); + } + R_TRY(this->ActivateContentStorage(StorageId::BuiltInSystem)); + + /* Next, the BuiltInSystem content meta entry. */ + R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount)); + + if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) { + R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem)); + + /* Try to build or import a database, depending on our configuration. */ + if (config.ShouldBuildDatabase()) { + /* If we should build the database, do so. */ + R_TRY(this->BuildContentMetaDatabase(StorageId::BuiltInSystem)); + R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem)); + } else if (config.ShouldImportDatabaseFromSignedSystemPartitionOnSd()) { + /* Otherwise if we should import the database from the SD, do so. */ + R_TRY(this->ImportContentMetaDatabase(StorageId::BuiltInSystem, true)); + R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem)); + } + } + + /* Ensure correct flags on the BuiltInSystem save data. */ + /* NOTE: Nintendo does not check this succeeds, and it does on older system versions. */ + /* We will not check the error, either, even though this kind of defeats the call's purpose. */ + if (hos::GetVersion() >= hos::Version_200) { + EnsureBuiltInSystemSaveDataFlags(); + } + + R_TRY(this->ActivateContentMetaDatabase(StorageId::BuiltInSystem)); + + /* Now for BuiltInUser's content storage and content meta entries. */ + R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[this->num_content_storage_entries++], StorageId::BuiltInUser, fs::ContentStorageId::User)); + R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount)); + + /* Beyond this point, N uses hardcoded indices. */ + + /* Next SdCard's content storage and content meta entries. */ + R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[2], StorageId::SdCard, fs::ContentStorageId::SdCard)); + R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount)); + + /* GameCard's content storage and content meta entries. */ + /* N doesn't set a content storage id for game cards, so we'll just use 0 (System). */ + R_TRY(this->InitializeGameCardContentStorageRoot(&this->content_storage_roots[3])); + R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], MaxGameCardContentMetaCount)); + + this->initialized = true; + return ResultSuccess(); + } + + Result ContentManagerImpl::CreateContentStorage(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content storage root. */ + ContentStorageRoot *root; + R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id)); + + /* Mount the relevant content storage. */ + R_TRY(fs::MountContentStorage(root->mount_name, root->content_storage_id)); + ON_SCOPE_EXIT { fs::Unmount(root->mount_name); }; + + /* Ensure the content storage root's path exists. */ + R_TRY(impl::EnsureDirectoryRecursively(root->path)); + + /* Initialize content and placeholder directories for the root. */ + return ContentStorageImpl::InitializeBase(root->path); + } + + Result ContentManagerImpl::CreateContentMetaDatabase(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + R_UNLESS(storage_id != StorageId::GameCard, ncm::ResultUnknownStorage()); + + /* Obtain the content meta database root. */ + ContentMetaDatabaseRoot *root; + R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id)); + + /* Mount (and optionally create) save data for the root. */ + R_TRY(this->EnsureAndMountSystemSaveData(root->mount_name, root->info)); + ON_SCOPE_EXIT { fs::Unmount(root->mount_name); }; + + /* Ensure the content meta database root's path exists. */ + R_TRY(impl::EnsureDirectoryRecursively(root->path)); + + /* Commit our changes. */ + return fs::CommitSaveData(root->mount_name); + } + + Result ContentManagerImpl::VerifyContentStorage(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content storage root. */ + ContentStorageRoot *root; + R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id)); + + /* Substitute the mount name in the root's path with a unique one. */ + char path[0x80]; + auto mount_name = impl::CreateUniqueMountName(); + ReplaceMountName(path, mount_name.str, root->path); + + /* Mount the relevant content storage. */ + R_TRY(fs::MountContentStorage(mount_name.str, root->content_storage_id)); + ON_SCOPE_EXIT { fs::Unmount(mount_name.str); }; + + /* Ensure the root, content and placeholder directories exist for the storage. */ + return ContentStorageImpl::VerifyBase(path); + } + + Result ContentManagerImpl::VerifyContentMetaDatabase(StorageId storage_id) { + /* Game card content meta databases will always be valid. */ + R_SUCCEED_IF(storage_id == StorageId::GameCard); + + std::scoped_lock lk(this->mutex); + + /* Obtain the content meta database root. */ + ContentMetaDatabaseRoot *root; + R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id)); + + /* Mount save data for non-existing content meta databases. */ + const bool mount = !root->content_meta_database; + if (mount) { + R_TRY(fs::MountSystemSaveData(root->mount_name, root->info.space_id, root->info.id)); + } + auto mount_guard = SCOPE_GUARD { if (mount) { fs::Unmount(root->mount_name); } }; + + /* Ensure the root path exists. */ + bool has_dir = false; + R_TRY(impl::HasDirectory(&has_dir, root->path)); + R_UNLESS(has_dir, ncm::ResultInvalidContentMetaDatabase()); + + return ResultSuccess(); + } + + Result ContentManagerImpl::OpenContentStorage(sf::Out> out, StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content storage root. */ + ContentStorageRoot *root; + R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id)); + + if (hos::GetVersion() >= hos::Version_200) { + /* Obtain the content storage if already active. */ + R_UNLESS(root->content_storage, GetContentStorageNotActiveResult(storage_id)); + } else { + /* 1.0.0 activates content storages as soon as they are opened. */ + if (!root->content_storage) { + R_TRY(this->ActivateContentStorage(storage_id)); + } + } + + out.SetValue(std::shared_ptr(root->content_storage)); + return ResultSuccess(); + } + + Result ContentManagerImpl::OpenContentMetaDatabase(sf::Out> out, StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content meta database root. */ + ContentMetaDatabaseRoot *root; + R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id)); + + if (hos::GetVersion() >= hos::Version_200) { + /* Obtain the content meta database if already active. */ + R_UNLESS(root->content_meta_database, GetContentMetaDatabaseNotActiveResult(storage_id)); + } else { + /* 1.0.0 activates content meta databases as soon as they are opened. */ + if (!root->content_meta_database) { + R_TRY(this->ActivateContentMetaDatabase(storage_id)); + } + } + + out.SetValue(std::shared_ptr(root->content_meta_database)); + return ResultSuccess(); + } + + Result ContentManagerImpl::CloseContentStorageForcibly(StorageId storage_id) { + return this->InactivateContentStorage(storage_id); + } + + Result ContentManagerImpl::CloseContentMetaDatabaseForcibly(StorageId storage_id) { + return this->InactivateContentMetaDatabase(storage_id); + } + + Result ContentManagerImpl::CleanupContentMetaDatabase(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Disable and unmount content meta database root. */ + R_TRY(this->InactivateContentMetaDatabase(storage_id)); + + /* Obtain the content meta database root. */ + ContentMetaDatabaseRoot *root; + R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id)); + + /* Delete save data for the content meta database root. */ + return fs::DeleteSaveData(root->info.space_id, root->info.id); + } + + Result ContentManagerImpl::ActivateContentStorage(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content storage root. */ + ContentStorageRoot *root; + R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id)); + + /* Check if the storage is already activated. */ + R_SUCCEED_IF(root->content_storage != nullptr); + + /* Mount based on the storage type. */ + if (storage_id == StorageId::GameCard) { + fs::GameCardHandle handle; + R_TRY(fs::GetGameCardHandle(std::addressof(handle))); + R_TRY(fs::MountGameCardPartition(root->mount_name, handle, fs::GameCardPartition::Secure)); + } else { + R_TRY(fs::MountContentStorage(root->mount_name, root->content_storage_id)); + } + + /* Unmount on failure. */ + auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); }; + + if (storage_id == StorageId::GameCard) { + /* Game card content storage is read only. */ + auto content_storage = std::make_shared(); + R_TRY(content_storage->Initialize(root->path, MakeFlatContentFilePath)); + root->content_storage = std::move(content_storage); + } else { + /* Create a content storage. */ + auto content_storage = std::make_shared(); + + /* Initialize content storage with an appropriate path function. */ + switch (storage_id) { + case StorageId::BuiltInSystem: + R_TRY(content_storage->Initialize(root->path, MakeFlatContentFilePath, MakeFlatPlaceHolderFilePath, false, std::addressof(this->rights_id_cache))); + break; + case StorageId::SdCard: + R_TRY(content_storage->Initialize(root->path, MakeSha256HierarchicalContentFilePath_ForFat16KCluster, MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster, true, std::addressof(this->rights_id_cache))); + break; + default: + R_TRY(content_storage->Initialize(root->path, MakeSha256HierarchicalContentFilePath_ForFat16KCluster, MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster, false, std::addressof(this->rights_id_cache))); + break; + } + + root->content_storage = std::move(content_storage); + } + + /* Prevent unmounting. */ + mount_guard.Cancel(); + return ResultSuccess(); + } + + Result ContentManagerImpl::InactivateContentStorage(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content storage root. */ + ContentStorageRoot *root; + R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id)); + + /* Disable and unmount the content storage, if present. */ + if (root->content_storage) { + /* N doesn't bother checking the result of this */ + root->content_storage->DisableForcibly(); + root->content_storage = nullptr; + fs::Unmount(root->mount_name); + } + + return ResultSuccess(); + } + + Result ContentManagerImpl::ActivateContentMetaDatabase(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content meta database root. */ + ContentMetaDatabaseRoot *root; + R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id)); + + /* Already activated. */ + R_SUCCEED_IF(root->content_meta_database != nullptr); + + /* Make a new kvs. */ + root->kvs.emplace(); + + if (storage_id == StorageId::GameCard) { + /* Initialize the key value store. */ + R_TRY(root->kvs->Initialize(root->max_content_metas)); + + /* Create an on memory content meta database for game cards. */ + root->content_meta_database = std::make_shared(std::addressof(*root->kvs)); + } else { + /* Mount save data for this root. */ + R_TRY(fs::MountSystemSaveData(root->mount_name, root->info.space_id, root->info.id)); + + /* Unmount on failure. */ + auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); }; + + /* Initialize and load the key value store from the filesystem. */ + R_TRY(root->kvs->Initialize(root->path, root->max_content_metas)); + R_TRY(root->kvs->Load()); + + /* Create the content meta database. */ + root->content_meta_database = std::make_shared(std::addressof(*root->kvs), root->mount_name); + mount_guard.Cancel(); + } + + return ResultSuccess(); + } + + Result ContentManagerImpl::InactivateContentMetaDatabase(StorageId storage_id) { + std::scoped_lock lk(this->mutex); + + /* Obtain the content meta database root. */ + ContentMetaDatabaseRoot *root; + R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id)); + + /* Disable the content meta database, if present. */ + if (root->content_meta_database) { + /* N doesn't bother checking the result of this */ + root->content_meta_database->DisableForcibly(); + root->content_meta_database = nullptr; + root->kvs = std::nullopt; + + /* Also unmount, except in the case of game cards. */ + if (storage_id != StorageId::GameCard) { + fs::Unmount(root->mount_name); + } + } + + return ResultSuccess(); + } + + Result ContentManagerImpl::InvalidateRightsIdCache() { + this->rights_id_cache.Invalidate(); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp new file mode 100644 index 000000000..6a57acd10 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + namespace { + + void ConvertPackageContentMetaHeaderToContentMetaHeader(ContentMetaHeader *dst, const PackagedContentMetaHeader &src) { + /* Clear destination. */ + *dst = {}; + + /* Set converted fields. */ + dst->extended_header_size = src.extended_header_size; + dst->content_meta_count = src.content_meta_count; + dst->content_count = src.content_meta_count; + dst->attributes = src.attributes; + } + + } + + size_t PackagedContentMetaReader::CountDeltaFragments() const { + size_t count = 0; + for (size_t i = 0; i < this->GetContentCount(); i++) { + if (this->GetContentInfo(i)->GetType() == ContentType::DeltaFragment) { + count++; + } + } + return count; + } + + size_t PackagedContentMetaReader::CalculateConvertContentMetaSize() const { + const auto *header = this->GetHeader(); + return this->CalculateSizeImpl(header->extended_header_size, header->content_count + 1, header->content_meta_count, 0, false); + } + + void PackagedContentMetaReader::ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta) { + /* Ensure we have enough space to convert. */ + AMS_ABORT_UNLESS(size >= this->CalculateConvertContentMetaSize()); + + /* Prepare for conversion. */ + const auto *packaged_header = this->GetHeader(); + uintptr_t dst_addr = reinterpret_cast(dst); + + /* Convert the header. */ + ContentMetaHeader header; + ConvertPackageContentMetaHeaderToContentMetaHeader(std::addressof(header), *packaged_header); + header.content_count += 1; + + /* Don't include deltas. */ + if (packaged_header->type == ContentMetaType::Patch) { + header.content_count -= this->CountDeltaFragments(); + } + + /* Copy the header. */ + std::memcpy(reinterpret_cast(dst_addr), std::addressof(header), sizeof(header)); + dst_addr += sizeof(header); + + /* Copy the extended header. */ + std::memcpy(reinterpret_cast(dst_addr), reinterpret_cast(this->GetExtendedHeaderAddress()), packaged_header->extended_header_size); + dst_addr += packaged_header->extended_header_size; + + /* Copy the top level meta. */ + std::memcpy(reinterpret_cast(dst_addr), std::addressof(meta), sizeof(meta)); + dst_addr += sizeof(meta); + + /* Copy content infos. */ + for (size_t i = 0; i < this->GetContentCount(); i++) { + /* Don't copy any delta fragments. */ + if (packaged_header->type == ContentMetaType::Patch) { + if (this->GetContentInfo(i)->GetType() == ContentType::DeltaFragment) { + continue; + } + } + + /* Copy the current info. */ + std::memcpy(reinterpret_cast(dst_addr), std::addressof(this->GetContentInfo(i)->info), sizeof(ContentInfo)); + dst_addr += sizeof(ContentInfo); + } + + /* Copy content meta infos. */ + for (size_t i = 0; i < this->GetContentMetaCount(); i++) { + std::memcpy(reinterpret_cast(dst_addr), this->GetContentMetaInfo(i), sizeof(ContentMetaInfo)); + dst_addr += sizeof(ContentMetaInfo); + } + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp new file mode 100644 index 000000000..19f7c6ffb --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_content_meta_database_impl.hpp" + +namespace ams::ncm { + + Result ContentMetaDatabaseImpl::GetContentIdImpl(ContentId *out, const ContentMetaKey& key, ContentType type, std::optional id_offset) const { + R_TRY(this->EnsureEnabled()); + + /* Find the meta key. */ + const auto it = this->kvs->lower_bound(key); + R_UNLESS(it != this->kvs->end(), ncm::ResultContentMetaNotFound()); + R_UNLESS(it->GetKey().id == key.id, ncm::ResultContentMetaNotFound()); + + const auto found_key = it->GetKey(); + + /* Create a reader for this content meta. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, found_key)); + + ContentMetaReader reader(meta, meta_size); + + /* Find the content info. */ + const ContentInfo *content_info = nullptr; + if (id_offset) { + content_info = reader.GetContentInfo(type, *id_offset); + } else { + content_info = reader.GetContentInfo(type); + } + R_UNLESS(content_info != nullptr, ncm::ResultContentNotFound()); + + /* Save output. */ + *out = content_info->content_id; + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::Set(const ContentMetaKey &key, sf::InBuffer value) { + R_TRY(this->EnsureEnabled()); + return this->kvs->Set(key, value.GetPointer(), value.GetSize()); + } + + Result ContentMetaDatabaseImpl::Get(sf::Out out_size, const ContentMetaKey &key, sf::OutBuffer out_value) { + R_TRY(this->EnsureEnabled()); + + /* Get the entry from our key-value store. */ + size_t size; + R_TRY_CATCH(this->kvs->Get(std::addressof(size), out_value.GetPointer(), out_value.GetSize(), key)) { + R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound()) + } R_END_TRY_CATCH; + + out_size.SetValue(size); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::Remove(const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + R_TRY_CATCH(this->kvs->Remove(key)) { + R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetContentIdByType(sf::Out out_content_id, const ContentMetaKey &key, ContentType type) { + return this->GetContentIdImpl(out_content_id.GetPointer(), key, type, std::nullopt); + } + + Result ContentMetaDatabaseImpl::ListContentInfo(sf::Out out_count, const sf::OutArray &out_info, const ContentMetaKey &key, s32 offset) { + R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); + R_TRY(this->EnsureEnabled()); + + /* Obtain the content meta for the given key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Read content infos from the given offset up to the given count. */ + size_t count; + for (count = 0; count < out_info.GetSize() && count + offset < reader.GetContentCount(); count++) { + out_info[count] = *reader.GetContentInfo(offset + count); + } + + out_count.SetValue(count); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) { + R_TRY(this->EnsureEnabled()); + + size_t entries_total = 0; + size_t entries_written = 0; + + /* Iterate over all entries. */ + for (auto &entry : *this->kvs) { + const ContentMetaKey key = entry.GetKey(); + + /* Check if this entry matches the given filters. */ + if (!((meta_type == ContentMetaType::Unknown || key.type == meta_type) && (min <= key.id && key.id <= max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) { + continue; + } + + /* If application id is present, check if it matches the filter. */ + if (application_id != InvalidApplicationId) { + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Ensure application id matches, if present. */ + if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id && application_id != *entry_application_id) { + continue; + } + } + + /* Write the entry to the output buffer. */ + if (entries_written < out_info.GetSize()) { + out_info[entries_written++] = key; + } + entries_total++; + } + + out_entries_total.SetValue(entries_total); + out_entries_written.SetValue(entries_written); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out out_key, u64 id) { + R_TRY(this->EnsureEnabled()); + + std::optional found_key = std::nullopt; + + /* Find the last key with the desired program id. */ + for (auto entry = this->kvs->lower_bound(ContentMetaKey::MakeUnknownType(id, 0)); entry != this->kvs->end(); entry++) { + /* No further entries will match the program id, discontinue. */ + if (entry->GetKey().id != id) { + break; + } + + /* We are only interested in keys with the Full content install type. */ + if (entry->GetKey().install_type == ContentInstallType::Full) { + found_key = entry->GetKey(); + } + } + + /* Check if the key is absent. */ + R_UNLESS(found_key, ncm::ResultContentMetaNotFound()); + + out_key.SetValue(*found_key); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::ListApplication(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_keys, ContentMetaType type) { + R_TRY(this->EnsureEnabled()); + + size_t entries_total = 0; + size_t entries_written = 0; + + /* Iterate over all entries. */ + for (auto &entry : *this->kvs) { + const ContentMetaKey key = entry.GetKey(); + + /* Check if this entry matches the given filters. */ + if (!(type == ContentMetaType::Unknown || key.type == type)) { + continue; + } + + /* Check if the entry has an application id. */ + ContentMetaReader reader(entry.GetValuePointer(), entry.GetValueSize()); + + if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id) { + /* Write the entry to the output buffer. */ + if (entries_written < out_keys.GetSize()) { + out_keys[entries_written++] = { key, *entry_application_id }; + } + entries_total++; + } + } + + out_entries_total.SetValue(entries_total); + out_entries_written.SetValue(entries_written); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::Has(sf::Out out, const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + *out = false; + + /* Check if key is present. */ + size_t size; + R_TRY_CATCH(this->kvs->GetValueSize(&size, key)) { + R_CONVERT(kvdb::ResultKeyNotFound, ResultSuccess()); + } R_END_TRY_CATCH; + + *out = true; + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::HasAll(sf::Out out, const sf::InArray &keys) { + R_TRY(this->EnsureEnabled()); + *out = false; + + /* Check if keys are present. */ + for (size_t i = 0; i < keys.GetSize(); i++) { + /* Check if we have the current key. */ + bool has; + R_TRY(this->Has(std::addressof(has), keys[i])); + + /* If we don't, then we can early return because we don't have all. */ + R_SUCCEED_IF(!has); + } + + *out = true; + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetSize(sf::Out out_size, const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + /* Determine the content meta size for the key. */ + size_t size; + R_TRY(this->GetContentMetaSize(&size, key)); + + out_size.SetValue(size); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetRequiredSystemVersion(sf::Out out_version, const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + /* Only applications and patches have a required system version. */ + R_UNLESS(key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch, ncm::ResultInvalidContentMetaKey()); + + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Obtain the required system version. */ + switch (key.type) { + case ContentMetaType::Application: + out_version.SetValue(reader.GetExtendedHeader()->required_system_version); + break; + case ContentMetaType::Patch: + out_version.SetValue(reader.GetExtendedHeader()->required_system_version); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetPatchId(sf::Out out_patch_id, const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + /* Only applications can have patches. */ + R_UNLESS(key.type == ContentMetaType::Application, ncm::ResultInvalidContentMetaKey()); + + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Obtain the patch id. */ + out_patch_id.SetValue(reader.GetExtendedHeader()->patch_id); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::DisableForcibly() { + this->disabled = true; + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) { + R_TRY(this->EnsureEnabled()); + R_UNLESS(out_orphaned.GetSize() >= content_ids.GetSize(), ncm::ResultBufferInsufficient()); + + /* Default to orphaned for all content ids. */ + for (size_t i = 0; i < out_orphaned.GetSize(); i++) { + out_orphaned[i] = true; + } + + auto IsOrphanedContent = [](const sf::InArray &list, const ncm::ContentId &id) ALWAYS_INLINE_LAMBDA { + /* Check if any input content ids match our found content id. */ + for (size_t i = 0; i < list.GetSize(); i++) { + if (list[i] == id) { + return std::make_optional(i); + } + } + + /* TODO: C++20 (gcc 10) fixes ALWAYS_INLINE_LAMBDA in conjunction with trailing return types. */ + /* This should be changed to std::nullopt once possible. */ + return std::optional(std::nullopt); + }; + + /* Iterate over all entries. */ + for (auto &entry : *this->kvs) { + ContentMetaReader reader(entry.GetValuePointer(), entry.GetValueSize()); + + /* Check if any of this entry's content infos matches one of the content ids for lookup. */ + /* If they do, then the content id isn't orphaned. */ + for (size_t i = 0; i < reader.GetContentCount(); i++) { + if (auto found = IsOrphanedContent(content_ids, reader.GetContentInfo(i)->GetId()); found) { + out_orphaned[*found] = false; + } + } + } + + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::Commit() { + R_TRY(this->EnsureEnabled()); + + /* Save and commit. */ + R_TRY(this->kvs->Save()); + return fs::CommitSaveData(this->mount_name); + } + + Result ContentMetaDatabaseImpl::HasContent(sf::Out out, const ContentMetaKey &key, const ContentId &content_id) { + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Optimistically suppose that we will find the content. */ + out.SetValue(true); + + /* Check if any content infos contain a matching id. */ + for (size_t i = 0; i < reader.GetContentCount(); i++) { + R_SUCCEED_IF(content_id == reader.GetContentInfo(i)->GetId()); + } + + /* We didn't find a content info. */ + out.SetValue(false); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::ListContentMetaInfo(sf::Out out_entries_written, const sf::OutArray &out_meta_info, const ContentMetaKey &key, s32 offset) { + R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); + R_TRY(this->EnsureEnabled()); + + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Read content meta infos from the given offset up to the given count. */ + size_t count; + for (count = 0; count < out_meta_info.GetSize() && count + offset <= reader.GetContentMetaCount(); count++) { + out_meta_info[count] = *reader.GetContentMetaInfo(count + offset); + } + + /* Set the ouput value. */ + out_entries_written.SetValue(count); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Set the ouput value. */ + out_attributes.SetValue(reader.GetHeader()->attributes); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Get the required version. */ + u32 required_version; + switch (key.type) { + case ContentMetaType::AddOnContent: + required_version = reader.GetExtendedHeader()->required_application_version; + break; + case ContentMetaType::Application: + /* As of 9.0.0, applications can be dependent on a specific base application version. */ + AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_900); + required_version = reader.GetExtendedHeader()->required_application_version; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Set the ouput value. */ + out_version.SetValue(required_version); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) { + return this->GetContentIdImpl(out_content_id.GetPointer(), key, type, std::make_optional(id_offset)); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp new file mode 100644 index 000000000..180c1c481 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "ncm_content_meta_database_impl_base.hpp" + +namespace ams::ncm { + + class ContentMetaDatabaseImpl : public ContentMetaDatabaseImplBase { + public: + ContentMetaDatabaseImpl(ContentMetaKeyValueStore *kvs, const char *mount_name) : ContentMetaDatabaseImplBase(kvs, mount_name) { /* ... */ } + ContentMetaDatabaseImpl(ContentMetaKeyValueStore *kvs) : ContentMetaDatabaseImplBase(kvs) { /* ... */ } + private: + /* Helpers. */ + Result GetContentIdImpl(ContentId *out, const ContentMetaKey& key, ContentType type, std::optional id_offset) const; + public: + /* Actual commands. */ + virtual Result Set(const ContentMetaKey &key, sf::InBuffer value) override; + virtual Result Get(sf::Out out_size, const ContentMetaKey &key, sf::OutBuffer out_value) override; + virtual Result Remove(const ContentMetaKey &key) override; + virtual Result GetContentIdByType(sf::Out out_content_id, const ContentMetaKey &key, ContentType type) override; + virtual Result ListContentInfo(sf::Out out_entries_written, const sf::OutArray &out_info, const ContentMetaKey &key, s32 offset) override; + virtual Result List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) override; + virtual Result GetLatestContentMetaKey(sf::Out out_key, u64 id) override; + virtual Result ListApplication(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_keys, ContentMetaType meta_type) override; + virtual Result Has(sf::Out out, const ContentMetaKey &key) override; + virtual Result HasAll(sf::Out out, const sf::InArray &keys) override; + virtual Result GetSize(sf::Out out_size, const ContentMetaKey &key) override; + virtual Result GetRequiredSystemVersion(sf::Out out_version, const ContentMetaKey &key) override; + virtual Result GetPatchId(sf::Out out_patch_id, const ContentMetaKey &key) override; + virtual Result DisableForcibly() override; + virtual Result LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) override; + virtual Result Commit() override; + virtual Result HasContent(sf::Out out, const ContentMetaKey &key, const ContentId &content_id) override; + virtual Result ListContentMetaInfo(sf::Out out_entries_written, const sf::OutArray &out_meta_info, const ContentMetaKey &key, s32 offset) override; + virtual Result GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) override; + virtual Result GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) override; + virtual Result GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) override; + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp new file mode 100644 index 000000000..171180622 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class ContentMetaDatabaseImplBase : public IContentMetaDatabase { + NON_COPYABLE(ContentMetaDatabaseImplBase); + NON_MOVEABLE(ContentMetaDatabaseImplBase); + protected: + using ContentMetaKeyValueStore = ams::kvdb::MemoryKeyValueStore; + protected: + ContentMetaKeyValueStore *kvs; + char mount_name[fs::MountNameLengthMax + 1]; + bool disabled; + protected: + ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs) : kvs(kvs), disabled(false) { /* ... */ } + + ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs, const char *mount_name) : ContentMetaDatabaseImplBase(kvs) { + std::strcpy(this->mount_name, mount_name); + } + protected: + /* Helpers. */ + Result EnsureEnabled() const { + R_UNLESS(!this->disabled, ncm::ResultInvalidContentMetaDatabase()); + return ResultSuccess(); + } + + Result GetContentMetaSize(size_t *out, const ContentMetaKey &key) const { + R_TRY_CATCH(this->kvs->GetValueSize(out, key)) { + R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result GetContentMetaPointer(const void **out_value_ptr, size_t *out_size, const ContentMetaKey &key) const { + R_TRY(this->GetContentMetaSize(out_size, key)); + return this->kvs->GetValuePointer(reinterpret_cast(out_value_ptr), key); + } + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_type.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_type.cpp new file mode 100644 index 000000000..4b9ffdfbb --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_type.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + const char *GetContentMetaTypeString(ContentMetaType type) { + switch (type) { + case ContentMetaType::Application: return "Application"; + case ContentMetaType::Patch: return "Patch"; + case ContentMetaType::AddOnContent: return "AddOnContent"; + default: return "(unknown)"; + } + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp new file mode 100644 index 000000000..df9a9e0e8 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_fs_utils.hpp" + +namespace ams::ncm { + + namespace { + + using FilePathString = kvdb::BoundedString<64>; + + bool IsContentMetaFileName(const char *name) { + return impl::PathView(name).HasSuffix(".cnmt"); + } + + } + + Result ReadContentMetaPath(AutoBuffer *out, const char *path) { + /* Mount the content. */ + auto mount_name = impl::CreateUniqueMountName(); + R_TRY(fs::MountContent(mount_name.str, path, fs::ContentType_Meta)); + ON_SCOPE_EXIT { fs::Unmount(mount_name.str); }; + + /* Open the root directory. */ + auto root_path = impl::GetRootDirectoryPath(mount_name); + fs::DirectoryHandle dir; + R_TRY(fs::OpenDirectory(std::addressof(dir), root_path.str, fs::OpenDirectoryMode_File)); + ON_SCOPE_EXIT { fs::CloseDirectory(dir); }; + + /* Loop directory reading until we find the entry we're looking for. */ + while (true) { + /* Read one entry, and finish when we fail to read. */ + fs::DirectoryEntry entry; + s64 num_read; + R_TRY(fs::ReadDirectory(std::addressof(num_read), std::addressof(entry), dir, 1)); + if (num_read == 0) { + break; + } + + /* If this is the content meta file, parse it. */ + if (IsContentMetaFileName(entry.name)) { + /* Create the file path. */ + FilePathString file_path(root_path.str); + file_path.Append(entry.name); + + /* Open the content meta file. */ + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), file_path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Get the meta size. */ + s64 file_size; + R_TRY(fs::GetFileSize(std::addressof(file_size), file)); + const size_t meta_size = static_cast(file_size); + + /* Create a buffer for the meta. */ + R_TRY(out->Initialize(meta_size)); + + /* Read the meta into the buffer. */ + return fs::ReadFile(file, 0, out->Get(), meta_size); + } + } + + return ncm::ResultContentMetaNotFound(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp new file mode 100644 index 000000000..8c4bfe113 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp @@ -0,0 +1,760 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_content_storage_impl.hpp" +#include "ncm_fs_utils.hpp" + +namespace ams::ncm { + + namespace { + + constexpr inline const char * const BaseContentDirectory = "/registered"; + + void MakeBaseContentDirectoryPath(PathString *out, const char *root_path) { + out->SetFormat("%s%s", root_path, BaseContentDirectory); + } + + void MakeContentPath(PathString *out, ContentId id, MakeContentPathFunction func, const char *root_path) { + PathString path; + MakeBaseContentDirectoryPath(std::addressof(path), root_path); + func(out, id, path); + } + + Result EnsureContentDirectory(ContentId id, MakeContentPathFunction func, const char *root_path) { + PathString path; + MakeContentPath(std::addressof(path), id, func, root_path); + return impl::EnsureParentDirectoryRecursively(path); + } + + Result DeleteContentFile(ContentId id, MakeContentPathFunction func, const char *root_path) { + /* Create the content path. */ + PathString path; + MakeContentPath(std::addressof(path), id, func, root_path); + + /* Delete the content. */ + R_TRY_CATCH(fs::DeleteFile(path)) { + R_CONVERT(fs::ResultPathNotFound, ncm::ResultContentNotFound()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + template + Result TraverseDirectory(bool *out_should_continue, const char *root_path, int max_level, F f) { + /* If the level is zero, we're done. */ + R_SUCCEED_IF(max_level <= 0); + + /* On 1.0.0, NotRequireFileSize was not a valid open mode. */ + const auto open_dir_mode = hos::GetVersion() >= hos::Version_200 ? (fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize) : (fs::OpenDirectoryMode_All); + + /* Retry traversal upon request. */ + bool retry_dir_read = true; + while (retry_dir_read) { + retry_dir_read = false; + + /* Open the directory at the given path. All entry types are allowed. */ + fs::DirectoryHandle dir; + R_TRY(fs::OpenDirectory(std::addressof(dir), root_path, open_dir_mode)); + ON_SCOPE_EXIT { fs::CloseDirectory(dir); }; + + while (true) { + /* Read a single directory entry. */ + fs::DirectoryEntry entry; + s64 entry_count; + R_TRY(fs::ReadDirectory(std::addressof(entry_count), std::addressof(entry), dir, 1)); + + /* Directory has no entries to process. */ + if (entry_count == 0) { + break; + } + + /* Path of the current entry. */ + PathString current_path; + current_path.SetFormat("%s/%s", root_path, entry.name); + + /* Call the process function. */ + bool should_continue = true; + bool should_retry_dir_read = false; + R_TRY(f(&should_continue, &should_retry_dir_read, current_path, entry)); + + /* If the provided function wishes to terminate immediately, we should respect it. */ + if (!should_continue) { + *out_should_continue = false; + return ResultSuccess(); + } + + /* Mark for retry. */ + if (should_retry_dir_read) { + retry_dir_read = true; + break; + } + + /* If the entry is a directory, recurse. */ + if (entry.type == fs::DirectoryEntryType_Directory) { + R_TRY(TraverseDirectory(std::addressof(should_continue), current_path, max_level - 1, f)); + + if (!should_continue) { + *out_should_continue = false; + return ResultSuccess(); + } + } + } + } + + return ResultSuccess(); + } + + + template + Result TraverseDirectory(const char *root_path, int max_level, F f) { + bool should_continue = false; + return TraverseDirectory(std::addressof(should_continue), root_path, max_level, f); + } + + bool IsContentPath(const char *path) { + impl::PathView view(path); + + /* Ensure nca suffix. */ + if (!view.HasSuffix(".nca")) { + return false; + } + + /* File name should be the size of a content id plus the nca file extension. */ + auto file_name = view.GetFileName(); + if (file_name.length() != ContentIdStringLength + 4) { + return false; + } + + /* Ensure file name is comprised of hex characters. */ + for (size_t i = 0; i < ContentIdStringLength; i++) { + if (!std::isxdigit(static_cast(file_name[i]))) { + return false; + } + } + + return true; + } + + bool IsPlaceHolderPath(const char *path) { + return IsContentPath(path); + } + + Result CleanDirectoryRecursively(const PathString &path) { + if (hos::GetVersion() >= hos::Version_300) { + R_TRY(fs::CleanDirectoryRecursively(path)); + } else { + /* CleanDirectoryRecursively didn't exist on < 3.0.0, so we will polyfill it. */ + /* We'll delete the directory, then recreate it. */ + R_TRY(fs::DeleteDirectoryRecursively(path)); + R_TRY(fs::CreateDirectory(path)); + } + return ResultSuccess(); + } + + } + + ContentStorageImpl::~ContentStorageImpl() { + this->InvalidateFileCache(); + } + + Result ContentStorageImpl::InitializeBase(const char *root_path) { + PathString path; + + /* Create the content directory. */ + MakeBaseContentDirectoryPath(std::addressof(path), root_path); + R_TRY(impl::EnsureDirectoryRecursively(path)); + + /* Create the placeholder directory. */ + PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path); + return impl::EnsureDirectoryRecursively(path); + } + + Result ContentStorageImpl::CleanupBase(const char *root_path) { + PathString path; + + /* Create the content directory. */ + MakeBaseContentDirectoryPath(std::addressof(path), root_path); + R_TRY(CleanDirectoryRecursively(path)); + + /* Create the placeholder directory. */ + PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path); + return CleanDirectoryRecursively(path); + } + + Result ContentStorageImpl::VerifyBase(const char *root_path) { + PathString path; + + /* Check if root directory exists. */ + bool has_dir; + R_TRY(impl::HasDirectory(std::addressof(has_dir), root_path)); + R_UNLESS(has_dir, ncm::ResultContentStorageBaseNotFound()); + + /* Check if content directory exists. */ + bool has_registered; + MakeBaseContentDirectoryPath(std::addressof(path), root_path); + R_TRY(impl::HasDirectory(std::addressof(has_registered), path)); + + /* Check if placeholder directory exists. */ + bool has_placeholder; + PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path); + R_TRY(impl::HasDirectory(std::addressof(has_placeholder), path)); + + /* Convert findings to results. */ + R_UNLESS(has_registered || has_placeholder, ncm::ResultContentStorageBaseNotFound()); + R_UNLESS(has_registered, ncm::ResultInvalidContentStorageBase()); + R_UNLESS(has_placeholder, ncm::ResultInvalidContentStorageBase()); + + return ResultSuccess(); + } + + void ContentStorageImpl::InvalidateFileCache() { + if (this->cached_content_id != InvalidContentId) { + fs::CloseFile(this->cached_file_handle); + this->cached_content_id = InvalidContentId; + } + } + + Result ContentStorageImpl::OpenContentIdFile(ContentId content_id) { + /* If the file is the currently cached one, we've nothing to do. */ + R_SUCCEED_IF(this->cached_content_id == content_id); + + /* Close any cached file. */ + this->InvalidateFileCache(); + + /* Create the content path. */ + PathString path; + MakeContentPath(std::addressof(path), content_id, this->make_content_path_func, this->root_path); + + /* Open the content file and store to the cache. */ + R_TRY_CATCH(fs::OpenFile(&this->cached_file_handle, path, fs::OpenMode_Read)) { + R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultContentNotFound()) + } R_END_TRY_CATCH; + + this->cached_content_id = content_id; + return ResultSuccess(); + } + + Result ContentStorageImpl::Initialize(const char *path, MakeContentPathFunction content_path_func, MakePlaceHolderPathFunction placeholder_path_func, bool delay_flush, RightsIdCache *rights_id_cache) { + R_TRY(this->EnsureEnabled()); + + /* Check paths exists for this content storage. */ + R_TRY(VerifyBase(path)); + + /* Initialize members. */ + this->root_path = PathString(path); + this->make_content_path_func = content_path_func; + this->placeholder_accessor.Initialize(std::addressof(this->root_path), placeholder_path_func, delay_flush); + this->rights_id_cache = rights_id_cache; + return ResultSuccess(); + } + + Result ContentStorageImpl::GeneratePlaceHolderId(sf::Out out) { + R_TRY(this->EnsureEnabled()); + out.SetValue({util::GenerateUuid()}); + return ResultSuccess(); + } + + Result ContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) { + R_TRY(this->EnsureEnabled()); + R_TRY(EnsureContentDirectory(content_id, this->make_content_path_func, this->root_path)); + return this->placeholder_accessor.CreatePlaceHolderFile(placeholder_id, size); + } + + Result ContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) { + R_TRY(this->EnsureEnabled()); + return this->placeholder_accessor.DeletePlaceHolderFile(placeholder_id); + } + + Result ContentStorageImpl::HasPlaceHolder(sf::Out out, PlaceHolderId placeholder_id) { + R_TRY(this->EnsureEnabled()); + + /* Create the placeholder path. */ + PathString placeholder_path; + this->placeholder_accessor.MakePath(std::addressof(placeholder_path), placeholder_id); + + /* Check if placeholder file exists. */ + bool has = false; + R_TRY(impl::HasFile(&has, placeholder_path)); + out.SetValue(has); + return ResultSuccess(); + } + + Result ContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, sf::InBuffer data) { + /* Ensure offset is valid. */ + R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); + R_TRY(this->EnsureEnabled()); + return this->placeholder_accessor.WritePlaceHolderFile(placeholder_id, offset, data.GetPointer(), data.GetSize()); + } + + Result ContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) { + this->InvalidateFileCache(); + R_TRY(this->EnsureEnabled()); + + /* Create the placeholder path. */ + PathString placeholder_path; + this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id); + + /* Create the content path. */ + PathString content_path; + MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + + /* Move the placeholder to the content path. */ + R_TRY_CATCH(fs::RenameFile(placeholder_path, content_path)) { + R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound()) + R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultContentAlreadyExists()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result ContentStorageImpl::Delete(ContentId content_id) { + R_TRY(this->EnsureEnabled()); + this->InvalidateFileCache(); + return DeleteContentFile(content_id, this->make_content_path_func, this->root_path); + } + + Result ContentStorageImpl::Has(sf::Out out, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Create the content path. */ + PathString content_path; + MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + + /* Check if the content file exists. */ + bool has = false; + R_TRY(impl::HasFile(&has, content_path)); + out.SetValue(has); + return ResultSuccess(); + } + + Result ContentStorageImpl::GetPath(sf::Out out, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Create the content path. */ + PathString content_path; + MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + + /* Substitute our mount name for the common mount name. */ + Path common_path; + R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), content_path)); + + out.SetValue(common_path); + return ResultSuccess(); + } + + Result ContentStorageImpl::GetPlaceHolderPath(sf::Out out, PlaceHolderId placeholder_id) { + R_TRY(this->EnsureEnabled()); + + /* Obtain the placeholder path. */ + PathString placeholder_path; + this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id); + + /* Substitute our mount name for the common mount name. */ + Path common_path; + R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), placeholder_path)); + + out.SetValue(common_path); + return ResultSuccess(); + } + + Result ContentStorageImpl::CleanupAllPlaceHolder() { + R_TRY(this->EnsureEnabled()); + + /* Clear the cache. */ + this->placeholder_accessor.InvalidateAll(); + + /* Obtain the placeholder base directory path. */ + PathString placeholder_dir; + PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(placeholder_dir), this->root_path); + + /* Cleanup the placeholder base directory. */ + CleanDirectoryRecursively(placeholder_dir); + return ResultSuccess(); + } + + Result ContentStorageImpl::ListPlaceHolder(sf::Out out_count, const sf::OutArray &out_buf) { + R_TRY(this->EnsureEnabled()); + + /* Obtain the placeholder base directory path. */ + PathString placeholder_dir; + PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(placeholder_dir), this->root_path); + + const size_t max_entries = out_buf.GetSize(); + size_t entry_count = 0; + + /* Traverse the placeholder base directory finding valid placeholder files. */ + R_TRY(TraverseDirectory(placeholder_dir, placeholder_accessor.GetHierarchicalDirectoryDepth(), [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) -> Result { + *should_continue = true; + *should_retry_dir_read = false; + + /* We are only looking for files. */ + if (entry.type == fs::DirectoryEntryType_File) { + R_UNLESS(entry_count <= max_entries, ncm::ResultBufferInsufficient()); + + /* Get the placeholder id from the filename. */ + PlaceHolderId placeholder_id; + R_TRY(PlaceHolderAccessor::GetPlaceHolderIdFromFileName(std::addressof(placeholder_id), entry.name)); + + out_buf[entry_count++] = placeholder_id; + } + + return ResultSuccess(); + })); + + out_count.SetValue(static_cast(entry_count)); + return ResultSuccess(); + } + + Result ContentStorageImpl::GetContentCount(sf::Out out_count) { + R_TRY(this->EnsureEnabled()); + + /* Obtain the content base directory path. */ + PathString path; + MakeBaseContentDirectoryPath(std::addressof(path), this->root_path); + + const auto depth = GetHierarchicalContentDirectoryDepth(this->make_content_path_func); + size_t count = 0; + + /* Traverse the content base directory finding all files. */ + R_TRY(TraverseDirectory(path, depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) -> Result { + *should_continue = true; + *should_retry_dir_read = false; + + /* Increment the count for each file found. */ + if (entry.type == fs::DirectoryEntryType_File) { + count++; + } + + return ResultSuccess(); + })); + + out_count.SetValue(static_cast(count)); + return ResultSuccess(); + } + + Result ContentStorageImpl::ListContentId(sf::Out out_count, const sf::OutArray &out_buf, s32 offset) { + R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); + R_TRY(this->EnsureEnabled()); + + /* Obtain the content base directory path. */ + PathString path; + MakeBaseContentDirectoryPath(std::addressof(path), this->root_path); + + const auto depth = GetHierarchicalContentDirectoryDepth(this->make_content_path_func); + size_t entry_count = 0; + + /* Traverse the content base directory finding all valid content. */ + R_TRY(TraverseDirectory(path, depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) { + *should_retry_dir_read = false; + *should_continue = true; + + /* We have nothing to do if not working with a file. */ + if (entry.type != fs::DirectoryEntryType_File) { + return ResultSuccess(); + } + + /* Skip entries until we reach the start offset. */ + if (offset > 0) { + --offset; + return ResultSuccess(); + } + + /* We don't necessarily expect to be able to completely fill the output buffer. */ + if (entry_count >= out_buf.GetSize()) { + *should_continue = false; + return ResultSuccess(); + } + + auto content_id = GetContentIdFromString(entry.name, std::strlen(entry.name)); + if (content_id) { + out_buf[entry_count++] = *content_id; + } + + return ResultSuccess(); + })); + + out_count.SetValue(static_cast(entry_count)); + return ResultSuccess(); + } + + Result ContentStorageImpl::GetSizeFromContentId(sf::Out out_size, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Create the content path. */ + PathString content_path; + MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + + /* Open the content file. */ + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), content_path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Obtain the size of the content. */ + s64 file_size; + R_TRY(fs::GetFileSize(std::addressof(file_size), file)); + + out_size.SetValue(file_size); + return ResultSuccess(); + } + + Result ContentStorageImpl::DisableForcibly() { + this->disabled = true; + this->InvalidateFileCache(); + this->placeholder_accessor.InvalidateAll(); + return ResultSuccess(); + } + + Result ContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) { + R_TRY(this->EnsureEnabled()); + + /* Close any cached file. */ + this->InvalidateFileCache(); + + /* Ensure the future content directory exists. */ + R_TRY(EnsureContentDirectory(new_content_id, this->make_content_path_func, this->root_path)); + + /* Ensure the destination placeholder directory exists. */ + R_TRY(this->placeholder_accessor.EnsurePlaceHolderDirectory(placeholder_id)); + + /* Obtain the placeholder path. */ + PathString placeholder_path; + this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id); + + /* Make the old content path. */ + PathString content_path; + MakeContentPath(std::addressof(content_path), old_content_id, this->make_content_path_func, this->root_path); + + /* Move the content to the placeholder path. */ + R_TRY_CATCH(fs::RenameFile(content_path, placeholder_path)) { + R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound()) + R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultContentAlreadyExists()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result ContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) { + R_TRY(this->EnsureEnabled()); + return this->placeholder_accessor.SetPlaceHolderFileSize(placeholder_id, size); + } + + Result ContentStorageImpl::ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, s64 offset) { + /* Ensure offset is valid. */ + R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); + R_TRY(this->EnsureEnabled()); + + /* Create the content path. */ + PathString content_path; + MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + + /* Open the content file. */ + R_TRY(this->OpenContentIdFile(content_id)); + + /* Read from the requested offset up to the requested size. */ + return fs::ReadFile(this->cached_file_handle, offset, buf.GetPointer(), buf.GetSize()); + } + + Result ContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated(sf::Out out_rights_id, PlaceHolderId placeholder_id) { + /* Obtain the regular rights id for the placeholder id. */ + ncm::RightsId rights_id; + R_TRY(this->GetRightsIdFromPlaceHolderId(&rights_id, placeholder_id)); + + /* Output the fs rights id. */ + out_rights_id.SetValue(rights_id.id); + return ResultSuccess(); + } + + Result ContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out out_rights_id, PlaceHolderId placeholder_id) { + R_TRY(this->EnsureEnabled()); + + /* Get the placeholder path. */ + Path path; + R_TRY(this->GetPlaceHolderPath(std::addressof(path), placeholder_id)); + + /* Get the rights id for the placeholder id. */ + return GetRightsId(out_rights_id.GetPointer(), path); + } + + Result ContentStorageImpl::GetRightsIdFromContentIdDeprecated(sf::Out out_rights_id, ContentId content_id) { + /* Obtain the regular rights id for the content id. */ + ncm::RightsId rights_id; + R_TRY(this->GetRightsIdFromContentId(&rights_id, content_id)); + + /* Output the fs rights id. */ + out_rights_id.SetValue(rights_id.id); + return ResultSuccess(); + } + + Result ContentStorageImpl::GetRightsIdFromContentId(sf::Out out_rights_id, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Attempt to obtain the rights id from the cache. */ + if (this->rights_id_cache->Find(out_rights_id.GetPointer(), content_id)) { + return ResultSuccess(); + } + + /* Get the path of the content. */ + Path path; + R_TRY(this->GetPath(std::addressof(path), content_id)); + + /* Obtain the rights id for the content. */ + ncm::RightsId rights_id; + R_TRY(GetRightsId(std::addressof(rights_id), path)); + + /* Store the rights id to the cache. */ + this->rights_id_cache->Store(content_id, rights_id); + + out_rights_id.SetValue(rights_id); + return ResultSuccess(); + } + + Result ContentStorageImpl::WriteContentForDebug(ContentId content_id, s64 offset, sf::InBuffer data) { + /* Ensure offset is valid. */ + R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); + R_TRY(this->EnsureEnabled()); + + /* This command is for development hardware only. */ + AMS_ABORT_UNLESS(spl::IsDevelopmentHardware()); + + /* Close any cached file. */ + this->InvalidateFileCache(); + + /* Make the content path. */ + PathString path; + MakeContentPath(std::addressof(path), content_id, this->make_content_path_func, this->root_path); + + /* Open the content file. */ + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), path.Get(), fs::OpenMode_Write)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Write the provided data to the file. */ + return fs::WriteFile(file, offset, data.GetPointer(), data.GetSize(), fs::WriteOption::Flush); + } + + Result ContentStorageImpl::GetFreeSpaceSize(sf::Out out_size) { + return fs::GetFreeSpaceSize(out_size.GetPointer(), this->root_path); + } + + Result ContentStorageImpl::GetTotalSpaceSize(sf::Out out_size) { + return fs::GetTotalSpaceSize(out_size.GetPointer(), this->root_path); + } + + Result ContentStorageImpl::FlushPlaceHolder() { + this->placeholder_accessor.InvalidateAll(); + return ResultSuccess(); + } + + Result ContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out out_size, PlaceHolderId placeholder_id) { + R_TRY(this->EnsureEnabled()); + + /* Attempt to get the placeholder file size. */ + bool found = false; + s64 file_size = 0; + R_TRY(this->placeholder_accessor.TryGetPlaceHolderFileSize(std::addressof(found), std::addressof(file_size), placeholder_id)); + + /* Set the output if placeholder file is found. */ + if (found) { + out_size.SetValue(file_size); + return ResultSuccess(); + } + + /* Get the path of the placeholder. */ + PathString placeholder_path; + this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id); + + /* Open the placeholder file. */ + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), placeholder_path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Get the size of the placeholder file. */ + R_TRY(fs::GetFileSize(std::addressof(file_size), file)); + + out_size.SetValue(file_size); + return ResultSuccess(); + } + + Result ContentStorageImpl::RepairInvalidFileAttribute() { + /* Callback for TraverseDirectory */ + using PathChecker = bool (*)(const char *); + PathChecker path_checker = nullptr; + + /* Set the archive bit appropriately for content/placeholders. */ + auto fix_file_attributes = [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) { + *should_retry_dir_read = false; + *should_continue = true; + + if (entry.type == fs::DirectoryEntryType_Directory) { + if (path_checker(current_path)) { + if (R_SUCCEEDED(fs::SetConcatenationFileAttribute(current_path))) { + *should_retry_dir_read = true; + } + } + } + + return ResultSuccess(); + }; + + /* Fix content. */ + { + path_checker = IsContentPath; + PathString path; + MakeBaseContentDirectoryPath(std::addressof(path), this->root_path); + + R_TRY(TraverseDirectory(path, GetHierarchicalContentDirectoryDepth(this->make_content_path_func), fix_file_attributes)); + } + + /* Fix placeholders. */ + this->placeholder_accessor.InvalidateAll(); + { + path_checker = IsPlaceHolderPath; + PathString path; + PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), this->root_path); + + R_TRY(TraverseDirectory(path, GetHierarchicalContentDirectoryDepth(this->make_content_path_func), fix_file_attributes)); + } + + return ResultSuccess(); + } + + Result ContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) { + R_TRY(this->EnsureEnabled()); + + /* Attempt to find the rights id in the cache. */ + if (this->rights_id_cache->Find(out_rights_id.GetPointer(), cache_content_id)) { + return ResultSuccess(); + } + + /* Get the placeholder path. */ + PathString placeholder_path; + this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id); + + /* Substitute mount name with the common mount name. */ + Path common_path; + R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), placeholder_path)); + + /* Get the rights id. */ + ncm::RightsId rights_id; + R_TRY(GetRightsId(&rights_id, common_path)); + this->rights_id_cache->Store(cache_content_id, rights_id); + + /* Set output. */ + out_rights_id.SetValue(rights_id); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp new file mode 100644 index 000000000..4d743c647 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#include "ncm_content_storage_impl_base.hpp" +#include "ncm_placeholder_accessor.hpp" + +namespace ams::ncm { + + class ContentStorageImpl : public ContentStorageImplBase { + protected: + PlaceHolderAccessor placeholder_accessor; + ContentId cached_content_id; + fs::FileHandle cached_file_handle; + RightsIdCache *rights_id_cache; + public: + static Result InitializeBase(const char *root_path); + static Result CleanupBase(const char *root_path); + static Result VerifyBase(const char *root_path); + public: + ~ContentStorageImpl(); + + Result Initialize(const char *root_path, MakeContentPathFunction content_path_func, MakePlaceHolderPathFunction placeholder_path_func, bool delay_flush, RightsIdCache *rights_id_cache); + private: + /* Helpers. */ + Result OpenContentIdFile(ContentId content_id); + void InvalidateFileCache(); + public: + /* Actual commands. */ + virtual Result GeneratePlaceHolderId(sf::Out out) override; + virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) override; + virtual Result DeletePlaceHolder(PlaceHolderId placeholder_id) override; + virtual Result HasPlaceHolder(sf::Out out, PlaceHolderId placeholder_id) override; + virtual Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, sf::InBuffer data) override; + virtual Result Register(PlaceHolderId placeholder_id, ContentId content_id) override; + virtual Result Delete(ContentId content_id) override; + virtual Result Has(sf::Out out, ContentId content_id) override; + virtual Result GetPath(sf::Out out, ContentId content_id) override; + virtual Result GetPlaceHolderPath(sf::Out out, PlaceHolderId placeholder_id) override; + virtual Result CleanupAllPlaceHolder() override; + virtual Result ListPlaceHolder(sf::Out out_count, const sf::OutArray &out_buf) override; + virtual Result GetContentCount(sf::Out out_count) override; + virtual Result ListContentId(sf::Out out_count, const sf::OutArray &out_buf, s32 start_offset) override; + virtual Result GetSizeFromContentId(sf::Out out_size, ContentId content_id) override; + virtual Result DisableForcibly() override; + virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) override; + virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) override; + virtual Result ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, s64 offset) override; + virtual Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out out_rights_id, PlaceHolderId placeholder_id) override; + virtual Result GetRightsIdFromPlaceHolderId(sf::Out out_rights_id, PlaceHolderId placeholder_id) override; + virtual Result GetRightsIdFromContentIdDeprecated(sf::Out out_rights_id, ContentId content_id) override; + virtual Result GetRightsIdFromContentId(sf::Out out_rights_id, ContentId content_id) override; + virtual Result WriteContentForDebug(ContentId content_id, s64 offset, sf::InBuffer data) override; + virtual Result GetFreeSpaceSize(sf::Out out_size) override; + virtual Result GetTotalSpaceSize(sf::Out out_size) override; + virtual Result FlushPlaceHolder() override; + virtual Result GetSizeFromPlaceHolderId(sf::Out out, PlaceHolderId placeholder_id) override; + virtual Result RepairInvalidFileAttribute() override; + virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) override; + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl_base.hpp b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl_base.hpp new file mode 100644 index 000000000..a8521edda --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl_base.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class ContentStorageImplBase : public IContentStorage { + NON_COPYABLE(ContentStorageImplBase); + NON_MOVEABLE(ContentStorageImplBase); + protected: + PathString root_path; + MakeContentPathFunction make_content_path_func; + bool disabled; + protected: + ContentStorageImplBase() { /* ... */ } + protected: + /* Helpers. */ + Result EnsureEnabled() const { + R_UNLESS(!this->disabled, ncm::ResultInvalidContentStorage()); + return ResultSuccess(); + } + + static Result GetRightsId(ncm::RightsId *out_rights_id, const Path &path) { + if (hos::GetVersion() >= hos::Version_300) { + R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), std::addressof(out_rights_id->key_generation), path.str)); + } else { + R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), path.str)); + out_rights_id->key_generation = 0; + } + return ResultSuccess(); + } + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp new file mode 100644 index 000000000..56881b558 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_fs_utils.hpp" + +namespace ams::ncm::impl { + + namespace { + + Result EnsureDirectory(const char *path) { + /* Create the path, and allow it to already exist. */ + R_TRY_CATCH(fs::CreateDirectory(path)) { + R_CONVERT(fs::ResultPathAlreadyExists, ResultSuccess()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result EnsureDirectoryRecursivelyImpl(const char *path, bool create_last) { + char work_buf[fs::EntryNameLengthMax]; + + /* Ensure the path is not too long. */ + const size_t len = std::strlen(path); + R_UNLESS(len + 1 <= sizeof(work_buf), ResultAllocationFailed()); + + /* Copy in the path. */ + std::strncpy(work_buf, path, sizeof(work_buf)); + + /* Create all but the last directory. */ + for (size_t i = 0; i < len; i++) { + if (i > 0 && fs::PathTool::IsSeparator(work_buf[i]) && !fs::PathTool::IsDriveSeparator(work_buf[i-1])) { + work_buf[i] = fs::StringTraits::NullTerminator; + R_TRY(EnsureDirectory(work_buf)); + work_buf[i] = fs::StringTraits::DirectorySeparator; + } + } + + /* Create the last directory if requested. */ + if (create_last) { + R_TRY(EnsureDirectory(path)); + } + + return ResultSuccess(); + } + + Result HasEntry(bool *out, const char *path, fs::DirectoryEntryType type) { + /* Set out to false initially. */ + *out = false; + + /* Try to get the entry type. */ + fs::DirectoryEntryType entry_type; + R_TRY_CATCH(fs::GetEntryType(std::addressof(entry_type), path)) { + /* If the path doesn't exist, nothing has gone wrong. */ + R_CONVERT(fs::ResultPathNotFound, ResultSuccess()); + } R_END_TRY_CATCH; + + /* We succeeded. */ + *out = entry_type == type; + return ResultSuccess(); + } + + std::atomic g_mount_name_count; + + } + + Result HasFile(bool *out, const char *path) { + return HasEntry(out, path, fs::DirectoryEntryType_File); + } + + Result HasDirectory(bool *out, const char *path) { + return HasEntry(out, path, fs::DirectoryEntryType_Directory); + } + + Result EnsureDirectoryRecursively(const char *path) { + return EnsureDirectoryRecursivelyImpl(path, true); + } + + Result EnsureParentDirectoryRecursively(const char *path) { + return EnsureDirectoryRecursivelyImpl(path, false); + } + + bool PathView::HasPrefix(std::string_view prefix) const { + return this->path.compare(0, prefix.length(), prefix) == 0; + } + + bool PathView::HasSuffix(std::string_view suffix) const { + return this->path.compare(this->path.length() - suffix.length(), suffix.length(), suffix) == 0; + } + + std::string_view PathView::GetFileName() const { + auto pos = this->path.find_last_of("/"); + return pos != std::string_view::npos ? this->path.substr(pos + 1) : this->path; + } + + MountName CreateUniqueMountName() { + MountName name = {}; + std::snprintf(name.str, sizeof(name.str), "@ncm%08x", g_mount_name_count.fetch_add(1)); + return name; + } + + RootDirectoryPath GetRootDirectoryPath(const MountName &mount_name) { + RootDirectoryPath path = {}; + std::snprintf(path.str, sizeof(path.str), "%s:/", mount_name.str); + + return path; + } + + Result CopyFile(const char *dst_path, const char *src_path) { + fs::FileHandle src_file, dst_file; + + /* Open the source file and get its size. */ + R_TRY(fs::OpenFile(std::addressof(src_file), src_path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(src_file); }; + + s64 file_size; + R_TRY(fs::GetFileSize(std::addressof(file_size), src_file)); + + /* Create the destination file. */ + R_TRY(fs::CreateFile(dst_path, file_size)); + + /* Open the destination file. */ + R_TRY(fs::OpenFile(std::addressof(dst_file), dst_path, fs::OpenMode_Write)); + ON_SCOPE_EXIT { fs::CloseFile(dst_file); }; + + /* Allocate a buffer with which to copy. */ + constexpr size_t BufferSize = 4_KB; + AutoBuffer buffer; + R_TRY(buffer.Initialize(BufferSize)); + + /* Repeatedly read until we've copied all the data. */ + s64 offset = 0; + while (offset < file_size) { + const size_t read_size = std::min(static_cast(file_size - offset), buffer.GetSize()); + R_TRY(fs::ReadFile(src_file, offset, buffer.Get(), read_size)); + R_TRY(fs::WriteFile(dst_file, offset, buffer.Get(), read_size, fs::WriteOption::None)); + offset += read_size; + } + + /* Flush the destination file. */ + R_TRY(fs::FlushFile(dst_file)); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp b/libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp new file mode 100644 index 000000000..438b2bde2 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm::impl { + + Result HasFile(bool *out, const char *path); + Result HasDirectory(bool *out, const char *path); + + Result EnsureDirectoryRecursively(const char *path); + Result EnsureParentDirectoryRecursively(const char *path); + + Result CopyFile(const char *dst_path, const char *src_path); + + class PathView { + private: + std::string_view path; /* Nintendo uses util::string_view here. */ + public: + PathView(std::string_view p) : path(p) { /* ...*/ } + bool HasPrefix(std::string_view prefix) const; + bool HasSuffix(std::string_view suffix) const; + std::string_view GetFileName() const; + }; + + struct MountName { + char str[fs::MountNameLengthMax + 1]; + }; + + struct RootDirectoryPath { + char str[fs::MountNameLengthMax + 3]; /* mount name + :/ */ + }; + + MountName CreateUniqueMountName(); + RootDirectoryPath GetRootDirectoryPath(const MountName &mount_name); + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_make_path.cpp b/libraries/libstratosphere/source/ncm/ncm_make_path.cpp new file mode 100644 index 000000000..594f912e6 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_make_path.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + namespace { + + void MakeContentName(PathString *out, ContentId id) { + out->SetFormat("%s.nca", GetContentIdString(id).data); + } + + void MakePlaceHolderName(PathString *out, PlaceHolderId id) { + auto &bytes = id.uuid.data; + char tmp[3]; + + /* Create a hex string from bytes. */ + for (size_t i = 0; i < sizeof(bytes); i++) { + std::snprintf(tmp, util::size(tmp), "%02x", bytes[i]); + out->Append(tmp); + } + + /* Append file extension. */ + out->Append(".nca"); + } + + u16 Get16BitSha256HashPrefix(ContentId id) { + u8 hash[crypto::Sha256Generator::HashSize]; + crypto::GenerateSha256Hash(hash, sizeof(hash), std::addressof(id), sizeof(id)); + return static_cast(hash[0]) | (static_cast(hash[1]) << 8); + } + + u8 Get8BitSha256HashPrefix(ContentId id) { + u8 hash[crypto::Sha256Generator::HashSize]; + crypto::GenerateSha256Hash(hash, sizeof(hash), std::addressof(id), sizeof(id)); + return hash[0]; + } + + u8 Get8BitSha256HashPrefix(PlaceHolderId id) { + u8 hash[crypto::Sha256Generator::HashSize]; + crypto::GenerateSha256Hash(hash, sizeof(hash), std::addressof(id), sizeof(id)); + return hash[0]; + } + + } + + void MakeFlatContentFilePath(PathString *out, ContentId content_id, const char *root_path) { + /* Create the content name from the content id. */ + PathString content_name; + MakeContentName(std::addressof(content_name), content_id); + + /* Format the output path. */ + out->SetFormat("%s/%s", root_path, content_name.Get()); + } + + void MakeSha256HierarchicalContentFilePath_ForFat4KCluster(PathString *out, ContentId content_id, const char *root_path) { + /* Hash the content id. */ + const u16 hash = Get16BitSha256HashPrefix(content_id); + const u32 hash_upper = (hash >> 10) & 0x3f; + const u32 hash_lower = (hash >> 4) & 0x3f; + + /* Create the content name from the content id. */ + PathString content_name; + MakeContentName(std::addressof(content_name), content_id); + + /* Format the output path. */ + out->SetFormat("%s/%08X/%08X/%s", root_path, hash_upper, hash_lower, content_name.Get()); + } + + void MakeSha256HierarchicalContentFilePath_ForFat32KCluster(PathString *out, ContentId content_id, const char *root_path) { + /* Hash the content id. */ + const u32 hash = (Get16BitSha256HashPrefix(content_id) >> 6) & 0x3FF; + + /* Create the content name from the content id. */ + PathString content_name; + MakeContentName(std::addressof(content_name), content_id); + + /* Format the output path. */ + out->SetFormat("%s/%08X/%s", root_path, hash, content_name.Get()); + } + + void MakeSha256HierarchicalContentFilePath_ForFat16KCluster(PathString *out, ContentId content_id, const char *root_path) { + /* Hash the content id. */ + const u32 hash_byte = static_cast(Get8BitSha256HashPrefix(content_id)); + + /* Create the content name from the content id. */ + PathString content_name; + MakeContentName(std::addressof(content_name), content_id); + + /* Format the output path. */ + out->SetFormat("%s/%08X/%s", root_path, hash_byte, content_name.Get()); + } + + size_t GetHierarchicalContentDirectoryDepth(MakeContentPathFunction func) { + if (func == MakeFlatContentFilePath) { + return 1; + } else if (func == MakeSha256HierarchicalContentFilePath_ForFat4KCluster) { + return 3; + } else if (func == MakeSha256HierarchicalContentFilePath_ForFat16KCluster || + func == MakeSha256HierarchicalContentFilePath_ForFat32KCluster) { + return 2; + } else { + AMS_ABORT(); + } + } + + void MakeFlatPlaceHolderFilePath(PathString *out, PlaceHolderId placeholder_id, const char *root_path) { + /* Create the placeholder name from the placeholder id. */ + PathString placeholder_name; + MakePlaceHolderName(std::addressof(placeholder_name), placeholder_id); + + /* Format the output path. */ + out->SetFormat("%s/%s", root_path, placeholder_name.Get()); + } + + void MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster(PathString *out, PlaceHolderId placeholder_id, const char *root_path) { + /* Hash the placeholder id. */ + const u32 hash_byte = static_cast(Get8BitSha256HashPrefix(placeholder_id)); + + /* Create the placeholder name from the placeholder id. */ + PathString placeholder_name; + MakePlaceHolderName(std::addressof(placeholder_name), placeholder_id); + + /* Format the output path. */ + out->SetFormat("%s/%08X/%s", root_path, hash_byte, placeholder_name.Get()); + } + + size_t GetHierarchicalPlaceHolderDirectoryDepth(MakePlaceHolderPathFunction func) { + if (func == MakeFlatPlaceHolderFilePath) { + return 1; + } else if (func == MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster) { + return 2; + } else { + AMS_ABORT(); + } + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp new file mode 100644 index 000000000..21af7d6ea --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_on_memory_content_meta_database_impl.hpp" + +namespace ams::ncm { + + Result OnMemoryContentMetaDatabaseImpl::List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) { + /* NOTE: This function is *almost* identical to the ContentMetaDatabaseImpl equivalent. */ + /* The only difference is that the min max comparison is exclusive for OnMemoryContentMetaDatabaseImpl, */ + /* but inclusive for ContentMetaDatabaseImpl. This may or may not be a Nintendo bug? */ + R_TRY(this->EnsureEnabled()); + + size_t entries_total = 0; + size_t entries_written = 0; + + /* Iterate over all entries. */ + for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) { + const ContentMetaKey key = entry->GetKey(); + + /* Check if this entry matches the given filters. */ + if (!((meta_type == ContentMetaType::Unknown || key.type == meta_type) && (min < key.id && key.id < max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) { + continue; + } + + /* If application id is present, check if it matches the filter. */ + if (application_id != InvalidApplicationId) { + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Ensure application id matches, if present. */ + if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id && application_id != *entry_application_id) { + continue; + } + } + + /* Write the entry to the output buffer. */ + if (entries_written < out_info.GetSize()) { + out_info[entries_written++] = key; + } + entries_total++; + } + + out_entries_total.SetValue(entries_total); + out_entries_written.SetValue(entries_written); + return ResultSuccess(); + } + + Result OnMemoryContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out out_key, u64 id) { + R_TRY(this->EnsureEnabled()); + + std::optional found_key = std::nullopt; + + /* Find the last key with the desired program id. */ + for (auto entry = this->kvs->lower_bound(ContentMetaKey::MakeUnknownType(id, 0)); entry != this->kvs->end(); entry++) { + /* No further entries will match the program id, discontinue. */ + if (entry->GetKey().id != id) { + break; + } + + /* On memory content database is interested in all keys. */ + found_key = entry->GetKey(); + } + + /* Check if the key is absent. */ + R_UNLESS(found_key, ncm::ResultContentMetaNotFound()); + + out_key.SetValue(*found_key); + return ResultSuccess(); + } + + Result OnMemoryContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) { + return ResultInvalidContentMetaDatabase(); + } + + Result OnMemoryContentMetaDatabaseImpl::Commit() { + R_TRY(this->EnsureEnabled()); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp new file mode 100644 index 000000000..9d2c88893 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "ncm_content_meta_database_impl.hpp" + +namespace ams::ncm { + + class OnMemoryContentMetaDatabaseImpl : public ContentMetaDatabaseImpl { + public: + OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore *kvs) : ContentMetaDatabaseImpl(kvs) { /* ... */ } + public: + /* Actual commands. */ + virtual Result List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) override; + virtual Result GetLatestContentMetaKey(sf::Out out_key, u64 id) override; + virtual Result LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) override; + virtual Result Commit() override; + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp new file mode 100644 index 000000000..e16771172 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_placeholder_accessor.hpp" +#include "ncm_fs_utils.hpp" + +namespace ams::ncm { + + namespace { + + constexpr inline const char * const BasePlaceHolderDirectory = "/placehld"; + + constexpr inline const char * const PlaceHolderExtension = ".nca"; + constexpr inline size_t PlaceHolderExtensionLength = 4; + + constexpr inline size_t PlaceHolderFileNameLengthWithoutExtension = 2 * sizeof(PlaceHolderId); + constexpr inline size_t PlaceHolderFileNameLength = PlaceHolderFileNameLengthWithoutExtension + PlaceHolderExtensionLength; + + void MakeBasePlaceHolderDirectoryPath(PathString *out, const char *root_path) { + out->SetFormat("%s%s", root_path, BasePlaceHolderDirectory); + } + + void MakePlaceHolderFilePath(PathString *out, PlaceHolderId id, MakePlaceHolderPathFunction func, const char *root_path) { + PathString path; + MakeBasePlaceHolderDirectoryPath(std::addressof(path), root_path); + func(out, id, path); + } + + ALWAYS_INLINE Result ConvertNotFoundResult(Result r) { + R_TRY_CATCH(r) { + R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound()) + } R_END_TRY_CATCH; + return ResultSuccess(); + } + + } + + void PlaceHolderAccessor::MakePath(PathString *out_placeholder_path, PlaceHolderId placeholder_id) const { + MakePlaceHolderFilePath(out_placeholder_path, placeholder_id, this->make_placeholder_path_func, *this->root_path); + } + + void PlaceHolderAccessor::MakeBaseDirectoryPath(PathString *out, const char *root_path) { + MakeBasePlaceHolderDirectoryPath(out, root_path); + } + + Result PlaceHolderAccessor::EnsurePlaceHolderDirectory(PlaceHolderId placeholder_id) { + PathString path; + this->MakePath(std::addressof(path), placeholder_id); + return impl::EnsureParentDirectoryRecursively(path); + } + + Result PlaceHolderAccessor::GetPlaceHolderIdFromFileName(PlaceHolderId *out, const char *name) { + /* Ensure placeholder name is valid. */ + R_UNLESS(strnlen(name, PlaceHolderFileNameLength) == PlaceHolderFileNameLength, ncm::ResultInvalidPlaceHolderFile()); + R_UNLESS(strncmp(name + PlaceHolderFileNameLengthWithoutExtension, PlaceHolderExtension, PlaceHolderExtensionLength) == 0, ncm::ResultInvalidPlaceHolderFile()); + + /* Convert each character pair to a byte until we reach the end. */ + PlaceHolderId placeholder_id = {}; + for (size_t i = 0; i < sizeof(placeholder_id); i++) { + char tmp[3]; + strlcpy(tmp, name + i * 2, sizeof(tmp)); + + char *err = nullptr; + reinterpret_cast(std::addressof(placeholder_id))[i] = static_cast(std::strtoul(tmp, std::addressof(err), 16)); + R_UNLESS(*err == '\x00', ncm::ResultInvalidPlaceHolderFile()); + } + + *out = placeholder_id; + return ResultSuccess(); + } + + Result PlaceHolderAccessor::Open(fs::FileHandle *out_handle, PlaceHolderId placeholder_id) { + /* Try to load from the cache. */ + R_SUCCEED_IF(this->LoadFromCache(out_handle, placeholder_id)); + + /* Make the path of the placeholder. */ + PathString placeholder_path; + this->MakePath(std::addressof(placeholder_path), placeholder_id); + + /* Open the placeholder file. */ + return fs::OpenFile(out_handle, placeholder_path, fs::OpenMode_Write); + } + + bool PlaceHolderAccessor::LoadFromCache(fs::FileHandle *out_handle, PlaceHolderId placeholder_id) { + std::scoped_lock lk(this->cache_mutex); + + /* Attempt to find an entry in the cache. */ + CacheEntry *entry = this->FindInCache(placeholder_id); + if (!entry) { + return false; + } + + /* No cached entry found. */ + entry->id = InvalidPlaceHolderId; + *out_handle = entry->handle; + return true; + } + + void PlaceHolderAccessor::StoreToCache(PlaceHolderId placeholder_id, fs::FileHandle handle) { + std::scoped_lock lk(this->cache_mutex); + + /* Store placeholder id and file handle to a free entry. */ + CacheEntry *entry = this->GetFreeEntry(); + entry->id = placeholder_id; + entry->handle = handle; + entry->counter = this->cur_counter++; + } + + void PlaceHolderAccessor::Invalidate(CacheEntry *entry) { + /* Flush and close the cached entry's file. */ + if (entry != nullptr) { + fs::FlushFile(entry->handle); + fs::CloseFile(entry->handle); + entry->id = InvalidPlaceHolderId; + } + } + + PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::FindInCache(PlaceHolderId placeholder_id) { + /* Ensure placeholder id is valid. */ + if (placeholder_id == InvalidPlaceHolderId) { + return nullptr; + } + + /* Attempt to find a cache entry with the same placeholder id. */ + for (size_t i = 0; i < MaxCacheEntries; i++) { + if (placeholder_id == this->caches[i].id) { + return &this->caches[i]; + } + } + return nullptr; + } + + PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::GetFreeEntry() { + /* Try to find an already free entry. */ + for (size_t i = 0; i < MaxCacheEntries; i++) { + if (this->caches[i].id == InvalidPlaceHolderId) { + return std::addressof(this->caches[i]); + } + } + + /* Get the oldest entry. */ + CacheEntry *entry = std::addressof(this->caches[0]); + for (size_t i = 1; i < MaxCacheEntries; i++) { + if (entry->counter < this->caches[i].counter) { + entry = std::addressof(this->caches[i]); + } + } + this->Invalidate(entry); + return entry; + } + + void PlaceHolderAccessor::GetPath(PathString *placeholder_path, PlaceHolderId placeholder_id) { + { + std::scoped_lock lock(this->cache_mutex); + this->Invalidate(this->FindInCache(placeholder_id)); + } + this->MakePath(placeholder_path, placeholder_id); + } + + Result PlaceHolderAccessor::CreatePlaceHolderFile(PlaceHolderId placeholder_id, s64 size) { + /* Ensure the destination directory exists. */ + R_TRY(this->EnsurePlaceHolderDirectory(placeholder_id)); + + /* Get the placeholder path. */ + PathString placeholder_path; + this->GetPath(std::addressof(placeholder_path), placeholder_id); + + /* Create the placeholder file. */ + R_TRY_CATCH(fs::CreateFile(placeholder_path, size, fs::CreateOption_BigFile)) { + R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultPlaceHolderAlreadyExists()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result PlaceHolderAccessor::DeletePlaceHolderFile(PlaceHolderId placeholder_id) { + /* Get the placeholder path. */ + PathString placeholder_path; + this->GetPath(std::addressof(placeholder_path), placeholder_id); + + /* Delete the placeholder file. */ + R_TRY_CATCH(fs::DeleteFile(placeholder_path)) { + R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result PlaceHolderAccessor::WritePlaceHolderFile(PlaceHolderId placeholder_id, s64 offset, const void *buffer, size_t size) { + /* Open the placeholder file. */ + fs::FileHandle file; + R_TRY_CATCH(this->Open(std::addressof(file), placeholder_id)) { + R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound()) + } R_END_TRY_CATCH; + + /* Store opened files to the cache regardless of write failures. */ + ON_SCOPE_EXIT { this->StoreToCache(placeholder_id, file); }; + + /* Write data to the placeholder file. */ + return fs::WriteFile(file, offset, buffer, size, this->delay_flush ? fs::WriteOption::Flush : fs::WriteOption::None); + } + + Result PlaceHolderAccessor::SetPlaceHolderFileSize(PlaceHolderId placeholder_id, s64 size) { + /* Open the placeholder file. */ + fs::FileHandle file; + R_TRY_CATCH(this->Open(std::addressof(file), placeholder_id)) { + R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound()) + } R_END_TRY_CATCH; + + /* Close the file on exit. */ + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Set the size of the placeholder file. */ + return fs::SetFileSize(file, size); + } + + Result PlaceHolderAccessor::TryGetPlaceHolderFileSize(bool *found_in_cache, s64 *out_size, PlaceHolderId placeholder_id) { + /* Attempt to find the placeholder in the cache. */ + fs::FileHandle handle; + auto found = this->LoadFromCache(std::addressof(handle), placeholder_id); + + if (found) { + /* Renew the entry in the cache. */ + this->StoreToCache(placeholder_id, handle); + R_TRY(fs::GetFileSize(out_size, handle)); + *found_in_cache = true; + } else { + *found_in_cache = false; + } + + return ResultSuccess(); + } + + void PlaceHolderAccessor::InvalidateAll() { + /* Invalidate all cache entries. */ + for (auto &entry : this->caches) { + if (entry.id != InvalidPlaceHolderId) { + this->Invalidate(std::addressof(entry)); + } + } + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp new file mode 100644 index 000000000..487e81f44 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class PlaceHolderAccessor { + private: + class CacheEntry { + public: + PlaceHolderId id; + fs::FileHandle handle; + u64 counter; + }; + + static constexpr size_t MaxCacheEntries = 0x2; + private: + std::array caches; + PathString *root_path; + u64 cur_counter; + os::Mutex cache_mutex; + MakePlaceHolderPathFunction make_placeholder_path_func; + bool delay_flush; + private: + Result Open(fs::FileHandle *out_handle, PlaceHolderId placeholder_id); + bool LoadFromCache(fs::FileHandle *out_handle, PlaceHolderId placeholder_id); + void StoreToCache(PlaceHolderId placeholder_id, fs::FileHandle handle); + void Invalidate(CacheEntry *entry); + CacheEntry *FindInCache(PlaceHolderId placeholder_id); + CacheEntry *GetFreeEntry();; + public: + PlaceHolderAccessor() : cur_counter(0), delay_flush(false) { + for (size_t i = 0; i < MaxCacheEntries; i++) { + caches[i].id = InvalidPlaceHolderId; + } + } + + ~PlaceHolderAccessor() { this->InvalidateAll(); } + + static void MakeBaseDirectoryPath(PathString *out, const char *root_path); + static Result GetPlaceHolderIdFromFileName(PlaceHolderId *out, const char *name); + public: + /* API. */ + void Initialize(PathString *root, MakePlaceHolderPathFunction path_func, bool delay_flush) { + this->root_path = root; + this->make_placeholder_path_func = path_func; + this->delay_flush = delay_flush; + } + + Result CreatePlaceHolderFile(PlaceHolderId placeholder_id, s64 size); + Result DeletePlaceHolderFile(PlaceHolderId placeholder_id); + Result WritePlaceHolderFile(PlaceHolderId placeholder_id, s64 offset, const void *buffer, size_t size); + Result SetPlaceHolderFileSize(PlaceHolderId placeholder_id, s64 size); + Result TryGetPlaceHolderFileSize(bool *out_found, s64 *out_size, PlaceHolderId placeholder_id); + + void GetPath(PathString *out_placeholder_path, PlaceHolderId placeholder_id); + void MakePath(PathString *out_placeholder_path, PlaceHolderId placeholder_id) const; + + void InvalidateAll(); + + Result EnsurePlaceHolderDirectory(PlaceHolderId placeholder_id); + size_t GetHierarchicalDirectoryDepth() const { return GetHierarchicalPlaceHolderDirectoryDepth(this->make_placeholder_path_func); } + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp new file mode 100644 index 000000000..c54505409 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "ncm_read_only_content_storage_impl.hpp" +#include "ncm_fs_utils.hpp" + +namespace ams::ncm { + + namespace { + + void MakeContentPath(PathString *out, ContentId id, MakeContentPathFunction func, const char *root_path) { + return func(out, id, root_path); + } + + void MakeGameCardContentMetaPath(PathString *out, ContentId id, MakeContentPathFunction func, const char *root_path) { + /* Determine the content path. */ + PathString path; + func(std::addressof(path), id, root_path); + + /* Substitute the .nca extension with .cmnt.nca. */ + *out = path.GetSubstring(0, path.GetLength() - 4); + out->Append(".cnmt.nca"); + } + + Result OpenContentIdFileImpl(fs::FileHandle *out, ContentId id, MakeContentPathFunction func, const char *root_path) { + PathString path; + MakeContentPath(std::addressof(path), id, func, root_path); + + /* Open the content file. */ + /* If absent, make the path for game card content meta and open again. */ + R_TRY_CATCH(fs::OpenFile(out, path, fs::OpenMode_Read)) { + R_CATCH(fs::ResultPathNotFound) { + MakeGameCardContentMetaPath(std::addressof(path), id, func, root_path); + R_TRY(fs::OpenFile(out, path, fs::OpenMode_Read)); + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + } + + Result ReadOnlyContentStorageImpl::Initialize(const char *path, MakeContentPathFunction content_path_func) { + R_TRY(this->EnsureEnabled()); + this->root_path.Set(path); + this->make_content_path_func = content_path_func; + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::GeneratePlaceHolderId(sf::Out out) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::HasPlaceHolder(sf::Out out, PlaceHolderId placeholder_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, sf::InBuffer data) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::Delete(ContentId content_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::Has(sf::Out out, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Make the content path. */ + PathString content_path; + MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + + /* Check if the file exists. */ + bool has; + R_TRY(impl::HasFile(std::addressof(has), content_path)); + + /* If the file is absent, make the path for game card content meta and check presence again. */ + if (!has) { + MakeGameCardContentMetaPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + R_TRY(impl::HasFile(std::addressof(has), content_path)); + } + + out.SetValue(has); + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::GetPath(sf::Out out, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Make the path for game card content meta. */ + PathString content_path; + MakeGameCardContentMetaPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + + /* Check if the file exists. */ + bool has_file; + R_TRY(impl::HasFile(std::addressof(has_file), content_path)); + + /* If the file is absent, make the path for regular content. */ + if (!has_file) { + MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); + } + + /* Substitute mount name with the common mount name. */ + Path common_path; + R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), content_path)); + + out.SetValue(common_path); + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::GetPlaceHolderPath(sf::Out out, PlaceHolderId placeholder_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::CleanupAllPlaceHolder() { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::ListPlaceHolder(sf::Out out_count, const sf::OutArray &out_buf) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::GetContentCount(sf::Out out_count) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::ListContentId(sf::Out out_count, const sf::OutArray &out_buf, s32 offset) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::GetSizeFromContentId(sf::Out out_size, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Open the file for the content id. */ + fs::FileHandle file; + R_TRY(OpenContentIdFileImpl(std::addressof(file), content_id, this->make_content_path_func, this->root_path)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Determine the file size. */ + s64 file_size; + R_TRY(fs::GetFileSize(std::addressof(file_size), file)); + + out_size.SetValue(file_size); + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::DisableForcibly() { + this->disabled = true; + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, s64 offset) { + /* Ensure offset is valid. */ + R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); + R_TRY(this->EnsureEnabled()); + + /* Open the file for the content id. */ + fs::FileHandle file; + R_TRY(OpenContentIdFileImpl(std::addressof(file), content_id, this->make_content_path_func, this->root_path)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Read from the given offset up to the given size. */ + R_TRY(fs::ReadFile(file, offset, buf.GetPointer(), buf.GetSize())); + + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated(sf::Out out_rights_id, PlaceHolderId placeholder_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out out_rights_id, PlaceHolderId placeholder_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::GetRightsIdFromContentIdDeprecated(sf::Out out_rights_id, ContentId content_id) { + /* Obtain the regular rights id for the content id. */ + ncm::RightsId rights_id; + R_TRY(this->GetRightsIdFromContentId(&rights_id, content_id)); + + /* Output the fs rights id. */ + out_rights_id.SetValue(rights_id.id); + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::GetRightsIdFromContentId(sf::Out out_rights_id, ContentId content_id) { + R_TRY(this->EnsureEnabled()); + + /* Get the content path. */ + Path path; + R_TRY(this->GetPath(std::addressof(path), content_id)); + + /* Get the rights id. */ + ncm::RightsId rights_id; + R_TRY(GetRightsId(&rights_id, path)); + out_rights_id.SetValue(rights_id); + + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::WriteContentForDebug(ContentId content_id, s64 offset, sf::InBuffer data) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::GetFreeSpaceSize(sf::Out out_size) { + out_size.SetValue(0); + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::GetTotalSpaceSize(sf::Out out_size) { + out_size.SetValue(0); + return ResultSuccess(); + } + + Result ReadOnlyContentStorageImpl::FlushPlaceHolder() { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out out, PlaceHolderId placeholder_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::RepairInvalidFileAttribute() { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + + Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) { + return ncm::ResultWriteToReadOnlyContentStorage(); + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.hpp new file mode 100644 index 000000000..74481bae8 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "ncm_content_storage_impl_base.hpp" + +namespace ams::ncm { + + class ReadOnlyContentStorageImpl : public ContentStorageImplBase { + public: + Result Initialize(const char *root_path, MakeContentPathFunction content_path_func); + public: + /* Actual commands. */ + virtual Result GeneratePlaceHolderId(sf::Out out) override; + virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) override; + virtual Result DeletePlaceHolder(PlaceHolderId placeholder_id) override; + virtual Result HasPlaceHolder(sf::Out out, PlaceHolderId placeholder_id) override; + virtual Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, sf::InBuffer data) override; + virtual Result Register(PlaceHolderId placeholder_id, ContentId content_id) override; + virtual Result Delete(ContentId content_id) override; + virtual Result Has(sf::Out out, ContentId content_id) override; + virtual Result GetPath(sf::Out out, ContentId content_id) override; + virtual Result GetPlaceHolderPath(sf::Out out, PlaceHolderId placeholder_id) override; + virtual Result CleanupAllPlaceHolder() override; + virtual Result ListPlaceHolder(sf::Out out_count, const sf::OutArray &out_buf) override; + virtual Result GetContentCount(sf::Out out_count) override; + virtual Result ListContentId(sf::Out out_count, const sf::OutArray &out_buf, s32 start_offset) override; + virtual Result GetSizeFromContentId(sf::Out out_size, ContentId content_id) override; + virtual Result DisableForcibly() override; + virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) override; + virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) override; + virtual Result ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, s64 offset) override; + virtual Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out out_rights_id, PlaceHolderId placeholder_id) override; + virtual Result GetRightsIdFromPlaceHolderId(sf::Out out_rights_id, PlaceHolderId placeholder_id) override; + virtual Result GetRightsIdFromContentIdDeprecated(sf::Out out_rights_id, ContentId content_id) override; + virtual Result GetRightsIdFromContentId(sf::Out out_rights_id, ContentId content_id) override; + virtual Result WriteContentForDebug(ContentId content_id, s64 offset, sf::InBuffer data) override; + virtual Result GetFreeSpaceSize(sf::Out out_size) override; + virtual Result GetTotalSpaceSize(sf::Out out_size) override; + virtual Result FlushPlaceHolder() override; + virtual Result GetSizeFromPlaceHolderId(sf::Out out, PlaceHolderId placeholder_id) override; + virtual Result RepairInvalidFileAttribute() override; + virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) override; + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp new file mode 100644 index 000000000..ac96d40bd --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "ncm_remote_content_storage_impl.hpp" +#include "ncm_remote_content_meta_database_impl.hpp" + +namespace ams::ncm { + + class RemoteContentManagerImpl final : public IContentManager { + public: + RemoteContentManagerImpl() { /* ... */ } + + ~RemoteContentManagerImpl() { /* ... */ } + public: + virtual Result CreateContentStorage(StorageId storage_id) override { + return ::ncmCreateContentStorage(static_cast(storage_id)); + } + + virtual Result CreateContentMetaDatabase(StorageId storage_id) override { + return ::ncmCreateContentMetaDatabase(static_cast(storage_id)); + } + + virtual Result VerifyContentStorage(StorageId storage_id) override { + return ::ncmVerifyContentStorage(static_cast(storage_id)); + } + + virtual Result VerifyContentMetaDatabase(StorageId storage_id) override { + return ::ncmVerifyContentMetaDatabase(static_cast(storage_id)); + } + + virtual Result OpenContentStorage(sf::Out> out, StorageId storage_id) override { + NcmContentStorage cs; + R_TRY(::ncmOpenContentStorage(std::addressof(cs), static_cast(storage_id))); + + out.SetValue(std::make_shared(cs)); + return ResultSuccess(); + } + + virtual Result OpenContentMetaDatabase(sf::Out> out, StorageId storage_id) override { + NcmContentMetaDatabase db; + R_TRY(::ncmOpenContentMetaDatabase(std::addressof(db), static_cast(storage_id))); + + out.SetValue(std::make_shared(db)); + return ResultSuccess(); + } + + virtual Result CloseContentStorageForcibly(StorageId storage_id) override { + return ::ncmCloseContentStorageForcibly(static_cast(storage_id)); + } + + virtual Result CloseContentMetaDatabaseForcibly(StorageId storage_id) override { + return ::ncmCloseContentMetaDatabaseForcibly(static_cast(storage_id)); + } + + virtual Result CleanupContentMetaDatabase(StorageId storage_id) override { + return ::ncmCleanupContentMetaDatabase(static_cast(storage_id)); + } + + virtual Result ActivateContentStorage(StorageId storage_id) override { + return ::ncmActivateContentStorage(static_cast(storage_id)); + } + + virtual Result InactivateContentStorage(StorageId storage_id) override { + return ::ncmInactivateContentStorage(static_cast(storage_id)); + } + + virtual Result ActivateContentMetaDatabase(StorageId storage_id) override { + return ::ncmActivateContentMetaDatabase(static_cast(storage_id)); + } + + virtual Result InactivateContentMetaDatabase(StorageId storage_id) override { + return ::ncmInactivateContentMetaDatabase(static_cast(storage_id)); + } + + virtual Result InvalidateRightsIdCache() override { + return ::ncmInvalidateRightsIdCache(); + } + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp new file mode 100644 index 000000000..9d8c72be4 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class RemoteContentMetaDatabaseImpl final : public IContentMetaDatabase { + private: + ::NcmContentMetaDatabase srv; + public: + RemoteContentMetaDatabaseImpl(::NcmContentMetaDatabase &db) : srv(db) { /* ... */ } + + ~RemoteContentMetaDatabaseImpl() { ::ncmContentMetaDatabaseClose(std::addressof(srv)); } + private: + ALWAYS_INLINE ::NcmContentMetaKey *Convert(ContentMetaKey *k) { + static_assert(sizeof(ContentMetaKey) == sizeof(::NcmContentMetaKey)); + return reinterpret_cast<::NcmContentMetaKey *>(k); + } + ALWAYS_INLINE const ::NcmContentMetaKey *Convert(const ContentMetaKey *k) { + static_assert(sizeof(ContentMetaKey) == sizeof(::NcmContentMetaKey)); + return reinterpret_cast(k); + } + + ALWAYS_INLINE const ::NcmContentMetaKey *Convert(const ContentMetaKey &k) { + static_assert(sizeof(ContentMetaKey) == sizeof(::NcmContentMetaKey)); + return reinterpret_cast(std::addressof(k)); + } + + ALWAYS_INLINE ::NcmApplicationContentMetaKey *Convert(ApplicationContentMetaKey *k) { + static_assert(sizeof(ApplicationContentMetaKey) == sizeof(::NcmApplicationContentMetaKey)); + return reinterpret_cast<::NcmApplicationContentMetaKey *>(k); + } + + ALWAYS_INLINE ::NcmContentInfo *Convert(ContentInfo *c) { + static_assert(sizeof(ContentInfo) == sizeof(::NcmContentInfo)); + return reinterpret_cast<::NcmContentInfo *>(c); + } + + ALWAYS_INLINE ::NcmContentId *Convert(ContentId *c) { + static_assert(sizeof(ContentId) == sizeof(::NcmContentId)); + return reinterpret_cast<::NcmContentId *>(c); + } + + ALWAYS_INLINE ::NcmContentId *Convert(ContentId &c) { + static_assert(sizeof(ContentId) == sizeof(::NcmContentId)); + return reinterpret_cast<::NcmContentId *>(std::addressof(c)); + } + + ALWAYS_INLINE const ::NcmContentId *Convert(const ContentId *c) { + static_assert(sizeof(ContentId) == sizeof(::NcmContentId)); + return reinterpret_cast(c); + } + + + ALWAYS_INLINE const ::NcmContentId *Convert(const ContentId &c) { + static_assert(sizeof(ContentId) == sizeof(::NcmContentId)); + return reinterpret_cast(std::addressof(c)); + } + public: + virtual Result Set(const ContentMetaKey &key, sf::InBuffer value) override { + return ncmContentMetaDatabaseSet(std::addressof(this->srv), Convert(key), value.GetPointer(), value.GetSize()); + } + + virtual Result Get(sf::Out out_size, const ContentMetaKey &key, sf::OutBuffer out_value) override { + return ncmContentMetaDatabaseGet(std::addressof(this->srv), Convert(key), out_size.GetPointer(), out_value.GetPointer(), out_value.GetSize()); + } + + virtual Result Remove(const ContentMetaKey &key) override { + return ncmContentMetaDatabaseRemove(std::addressof(this->srv), Convert(key)); + } + + virtual Result GetContentIdByType(sf::Out out_content_id, const ContentMetaKey &key, ContentType type) override { + return ncmContentMetaDatabaseGetContentIdByType(std::addressof(this->srv), Convert(out_content_id.GetPointer()), Convert(key), static_cast<::NcmContentType>(type)); + } + + virtual Result ListContentInfo(sf::Out out_entries_written, const sf::OutArray &out_info, const ContentMetaKey &key, s32 offset) override { + return ncmContentMetaDatabaseListContentInfo(std::addressof(this->srv), out_entries_written.GetPointer(), Convert(out_info.GetPointer()), out_info.GetSize(), Convert(key), offset); + } + + virtual Result List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) override { + return ncmContentMetaDatabaseList(std::addressof(this->srv), out_entries_total.GetPointer(), out_entries_written.GetPointer(), Convert(out_info.GetPointer()), out_info.GetSize(), static_cast<::NcmContentMetaType>(meta_type), application_id.value, min, max, static_cast<::NcmContentInstallType>(install_type)); + } + + virtual Result GetLatestContentMetaKey(sf::Out out_key, u64 id) override { + return ncmContentMetaDatabaseGetLatestContentMetaKey(std::addressof(this->srv), Convert(out_key.GetPointer()), static_cast(id)); + } + + virtual Result ListApplication(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_keys, ContentMetaType meta_type) override { + return ncmContentMetaDatabaseListApplication(std::addressof(this->srv), out_entries_total.GetPointer(), out_entries_written.GetPointer(), Convert(out_keys.GetPointer()), out_keys.GetSize(), static_cast<::NcmContentMetaType>(meta_type)); + } + + virtual Result Has(sf::Out out, const ContentMetaKey &key) override { + return ncmContentMetaDatabaseHas(std::addressof(this->srv), out.GetPointer(), Convert(key)); + } + + virtual Result HasAll(sf::Out out, const sf::InArray &keys) override { + return ncmContentMetaDatabaseHasAll(std::addressof(this->srv), out.GetPointer(), Convert(keys.GetPointer()), keys.GetSize()); + } + + virtual Result GetSize(sf::Out out_size, const ContentMetaKey &key) override { + return ncmContentMetaDatabaseGetSize(std::addressof(this->srv), out_size.GetPointer(), Convert(key)); + } + + virtual Result GetRequiredSystemVersion(sf::Out out_version, const ContentMetaKey &key) override { + return ncmContentMetaDatabaseGetRequiredSystemVersion(std::addressof(this->srv), out_version.GetPointer(), Convert(key)); + } + + virtual Result GetPatchId(sf::Out out_patch_id, const ContentMetaKey &key) override { + return ncmContentMetaDatabaseGetPatchId(std::addressof(this->srv), reinterpret_cast(out_patch_id.GetPointer()), Convert(key)); + } + + virtual Result DisableForcibly() override { + return ncmContentMetaDatabaseDisableForcibly(std::addressof(this->srv)); + } + + virtual Result LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) override { + return ncmContentMetaDatabaseLookupOrphanContent(std::addressof(this->srv), out_orphaned.GetPointer(), Convert(content_ids.GetPointer()), std::min(out_orphaned.GetSize(), content_ids.GetSize())); + } + + virtual Result Commit() override { + return ncmContentMetaDatabaseCommit(std::addressof(this->srv)); + } + + virtual Result HasContent(sf::Out out, const ContentMetaKey &key, const ContentId &content_id) override { + return ncmContentMetaDatabaseHasContent(std::addressof(this->srv), out.GetPointer(), Convert(key), Convert(content_id)); + } + + virtual Result ListContentMetaInfo(sf::Out out_entries_written, const sf::OutArray &out_meta_info, const ContentMetaKey &key, s32 offset) override { + return ncmContentMetaDatabaseListContentMetaInfo(std::addressof(this->srv), out_entries_written.GetPointer(), out_meta_info.GetPointer(), out_meta_info.GetSize(), Convert(key), offset); + } + + virtual Result GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) override { + static_assert(sizeof(ContentMetaAttribute) == sizeof(u8)); + return ncmContentMetaDatabaseGetAttributes(std::addressof(this->srv), Convert(key), out_attributes.GetPointer()); + } + + virtual Result GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) override { + return ncmContentMetaDatabaseGetRequiredApplicationVersion(std::addressof(this->srv), out_version.GetPointer(), Convert(key)); + } + + virtual Result GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) override { + return ncmContentMetaDatabaseGetContentIdByTypeAndIdOffset(std::addressof(this->srv), Convert(out_content_id.GetPointer()), Convert(key), static_cast<::NcmContentType>(type), id_offset); + } + + }; + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_remote_content_storage_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_remote_content_storage_impl.hpp new file mode 100644 index 000000000..a7db1d4d9 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_remote_content_storage_impl.hpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ncm { + + class RemoteContentStorageImpl final : public IContentStorage { + private: + ::NcmContentStorage srv; + public: + RemoteContentStorageImpl(::NcmContentStorage &cs) : srv(cs) { /* ... */ } + + ~RemoteContentStorageImpl() { ::ncmContentStorageClose(std::addressof(srv)); } + private: + ALWAYS_INLINE ::NcmPlaceHolderId *Convert(PlaceHolderId *p) { + static_assert(sizeof(PlaceHolderId) == sizeof(::NcmPlaceHolderId)); + return reinterpret_cast<::NcmPlaceHolderId *>(p); + } + + ALWAYS_INLINE ::NcmPlaceHolderId *Convert(PlaceHolderId &p) { + static_assert(sizeof(PlaceHolderId) == sizeof(::NcmPlaceHolderId)); + return reinterpret_cast<::NcmPlaceHolderId *>(std::addressof(p)); + } + + ALWAYS_INLINE ::NcmContentId *Convert(ContentId *c) { + static_assert(sizeof(ContentId) == sizeof(::NcmContentId)); + return reinterpret_cast<::NcmContentId *>(c); + } + + ALWAYS_INLINE ::NcmContentId *Convert(ContentId &c) { + static_assert(sizeof(ContentId) == sizeof(::NcmContentId)); + return reinterpret_cast<::NcmContentId *>(std::addressof(c)); + } + public: + virtual Result GeneratePlaceHolderId(sf::Out out) override { + return ncmContentStorageGeneratePlaceHolderId(std::addressof(this->srv), Convert(out.GetPointer())); + } + + virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) override { + static_assert(alignof(ContentId) < alignof(PlaceHolderId)); + return ncmContentStorageCreatePlaceHolder(std::addressof(this->srv), Convert(content_id), Convert(placeholder_id), size); + } + + virtual Result DeletePlaceHolder(PlaceHolderId placeholder_id) override { + return ncmContentStorageDeletePlaceHolder(std::addressof(this->srv), Convert(placeholder_id)); + } + + virtual Result HasPlaceHolder(sf::Out out, PlaceHolderId placeholder_id) override { + return ncmContentStorageHasPlaceHolder(std::addressof(this->srv), out.GetPointer(), Convert(placeholder_id)); + } + + virtual Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, sf::InBuffer data) override { + return ncmContentStorageWritePlaceHolder(std::addressof(this->srv), Convert(placeholder_id), offset, data.GetPointer(), data.GetSize()); + } + + virtual Result Register(PlaceHolderId placeholder_id, ContentId content_id) override { + static_assert(alignof(ContentId) < alignof(PlaceHolderId)); + return ncmContentStorageRegister(std::addressof(this->srv), Convert(content_id), Convert(placeholder_id)); + } + + virtual Result Delete(ContentId content_id) override { + return ncmContentStorageDelete(std::addressof(this->srv), Convert(content_id)); + } + + virtual Result Has(sf::Out out, ContentId content_id) override { + return ncmContentStorageHas(std::addressof(this->srv), out.GetPointer(), Convert(content_id)); + } + + virtual Result GetPath(sf::Out out, ContentId content_id) override { + return ncmContentStorageGetPath(std::addressof(this->srv), out.GetPointer()->str, sizeof(out.GetPointer()->str), Convert(content_id)); + } + + virtual Result GetPlaceHolderPath(sf::Out out, PlaceHolderId placeholder_id) override { + return ncmContentStorageGetPlaceHolderPath(std::addressof(this->srv), out.GetPointer()->str, sizeof(out.GetPointer()->str), Convert(placeholder_id)); + } + + virtual Result CleanupAllPlaceHolder() override { + return ncmContentStorageCleanupAllPlaceHolder(std::addressof(this->srv)); + } + + virtual Result ListPlaceHolder(sf::Out out_count, const sf::OutArray &out_buf) override { + return ncmContentStorageListPlaceHolder(std::addressof(this->srv), Convert(out_buf.GetPointer()), out_buf.GetSize(), out_count.GetPointer()); + } + + virtual Result GetContentCount(sf::Out out_count) override { + return ncmContentStorageGetContentCount(std::addressof(this->srv), out_count.GetPointer()); + } + + virtual Result ListContentId(sf::Out out_count, const sf::OutArray &out_buf, s32 offset) override { + return ncmContentStorageListContentId(std::addressof(this->srv), Convert(out_buf.GetPointer()), out_buf.GetSize(), out_count.GetPointer(), offset); + } + + virtual Result GetSizeFromContentId(sf::Out out_size, ContentId content_id) override { + return ncmContentStorageGetSizeFromContentId(std::addressof(this->srv), out_size.GetPointer(), Convert(content_id)); + } + + virtual Result DisableForcibly() override { + return ncmContentStorageDisableForcibly(std::addressof(this->srv)); + } + + virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) override { + return ncmContentStorageRevertToPlaceHolder(std::addressof(this->srv), Convert(placeholder_id), Convert(old_content_id), Convert(new_content_id)); + } + + virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) override { + return ncmContentStorageSetPlaceHolderSize(std::addressof(this->srv), Convert(placeholder_id), size); + } + + virtual Result ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, s64 offset) override { + return ncmContentStorageReadContentIdFile(std::addressof(this->srv), buf.GetPointer(), buf.GetSize(), Convert(content_id), offset); + } + + virtual Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out out_rights_id, PlaceHolderId placeholder_id) override { + ::NcmRightsId rights_id; + R_TRY(ncmContentStorageGetRightsIdFromPlaceHolderId(std::addressof(this->srv), std::addressof(rights_id), Convert(placeholder_id))); + + static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id)); + std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer())); + return ResultSuccess(); + } + + virtual Result GetRightsIdFromPlaceHolderId(sf::Out out_rights_id, PlaceHolderId placeholder_id) override { + ::NcmRightsId rights_id; + R_TRY(ncmContentStorageGetRightsIdFromPlaceHolderId(std::addressof(this->srv), std::addressof(rights_id), Convert(placeholder_id))); + + static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id)); + std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer())); + return ResultSuccess(); + } + + virtual Result GetRightsIdFromContentIdDeprecated(sf::Out out_rights_id, ContentId content_id) override { + ::NcmRightsId rights_id; + R_TRY(ncmContentStorageGetRightsIdFromContentId(std::addressof(this->srv), std::addressof(rights_id), Convert(content_id))); + + static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id)); + std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer())); + return ResultSuccess(); + } + + virtual Result GetRightsIdFromContentId(sf::Out out_rights_id, ContentId content_id) override { + ::NcmRightsId rights_id; + R_TRY(ncmContentStorageGetRightsIdFromContentId(std::addressof(this->srv), std::addressof(rights_id), Convert(content_id))); + + static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id)); + std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer())); + return ResultSuccess(); + } + + virtual Result WriteContentForDebug(ContentId content_id, s64 offset, sf::InBuffer data) override { + return ncmContentStorageWriteContentForDebug(std::addressof(this->srv), Convert(content_id), offset, data.GetPointer(), data.GetSize()); + } + + virtual Result GetFreeSpaceSize(sf::Out out_size) override { + return ncmContentStorageGetFreeSpaceSize(std::addressof(this->srv), out_size.GetPointer()); + } + + virtual Result GetTotalSpaceSize(sf::Out out_size) override { + return ncmContentStorageGetTotalSpaceSize(std::addressof(this->srv), out_size.GetPointer()); + } + + virtual Result FlushPlaceHolder() override { + return ncmContentStorageFlushPlaceHolder(std::addressof(this->srv)); + } + + virtual Result GetSizeFromPlaceHolderId(sf::Out out_size, PlaceHolderId placeholder_id) override { + return ncmContentStorageGetSizeFromPlaceHolderId(std::addressof(this->srv), out_size.GetPointer(), Convert(placeholder_id)); + } + + virtual Result RepairInvalidFileAttribute() override { + return ncmContentStorageRepairInvalidFileAttribute(std::addressof(this->srv)); + } + + virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) override { + static_assert(sizeof(::NcmRightsId) == sizeof(ncm::RightsId)); + ::NcmRightsId *out = reinterpret_cast<::NcmRightsId *>(out_rights_id.GetPointer()); + return ncmContentStorageGetRightsIdFromPlaceHolderIdWithCache(std::addressof(this->srv), out, Convert(placeholder_id), Convert(cache_content_id)); + } + }; + +} diff --git a/libraries/libstratosphere/source/updater/updater_api.cpp b/libraries/libstratosphere/source/updater/updater_api.cpp index 33c417188..55551c7e4 100644 --- a/libraries/libstratosphere/source/updater/updater_api.cpp +++ b/libraries/libstratosphere/source/updater/updater_api.cpp @@ -32,25 +32,26 @@ namespace ams::updater { /* Configuration Prototypes. */ bool HasEks(BootImageUpdateType boot_image_update_type); bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type); - NcmContentMetaType GetNcmContentMetaType(BootModeType mode); - Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size); + ncm::ContentMetaType GetContentMetaType(BootModeType mode); + Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size); /* Verification Prototypes. */ Result GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size); - Result VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - Result VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - Result VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result VerifyBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result VerifyBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result VerifyBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); /* Update Prototypes. */ Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size); - Result UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - Result UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - Result UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result UpdateBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result UpdateBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result UpdateBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); /* Package helpers. */ Result ValidateBctFileHash(Boot0Accessor &accessor, Boot0Partition which, const void *stored_hash, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); Result GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which); Result WritePackage2(void *work_buffer, size_t work_buffer_size, Package2Type which, BootImageUpdateType boot_image_update_type); + Result CompareHash(const void *lhs, const void *rhs, size_t size); /* Implementations. */ Result ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size) { @@ -80,12 +81,12 @@ namespace ams::updater { } } - NcmContentMetaType GetNcmContentMetaType(BootModeType mode) { + ncm::ContentMetaType GetContentMetaType(BootModeType mode) { switch (mode) { case BootModeType::Normal: - return NcmContentMetaType_BootImagePackage; + return ncm::ContentMetaType::BootImagePackage; case BootModeType::Safe: - return NcmContentMetaType_BootImagePackageSafe; + return ncm::ContentMetaType::BootImagePackageSafe; AMS_UNREACHABLE_DEFAULT_CASE(); } } @@ -114,8 +115,8 @@ namespace ams::updater { Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { /* Get system data id for boot images (819/81A/81B/81C). */ - u64 bip_data_id = 0; - R_TRY(GetBootImagePackageDataId(&bip_data_id, mode, work_buffer, work_buffer_size)); + ncm::SystemDataId bip_data_id; + R_TRY(GetBootImagePackageId(&bip_data_id, mode, work_buffer, work_buffer_size)); /* Verify the boot images in NAND. */ R_TRY_CATCH(VerifyBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type)) { @@ -130,47 +131,40 @@ namespace ams::updater { return SetVerificationNeeded(mode, false, work_buffer, work_buffer_size); } - Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) { + Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) { /* Ensure we can read content metas. */ constexpr size_t MaxContentMetas = 0x40; - AMS_ABORT_UNLESS(work_buffer_size >= sizeof(NcmContentMetaKey) * MaxContentMetas); + AMS_ABORT_UNLESS(work_buffer_size >= sizeof(ncm::ContentMetaKey) * MaxContentMetas); /* Open NAND System meta database, list contents. */ - NcmContentMetaDatabase meta_db; - R_TRY(ncmOpenContentMetaDatabase(&meta_db, NcmStorageId_BuiltInSystem)); - ON_SCOPE_EXIT { serviceClose(&meta_db.s); }; + ncm::ContentMetaDatabase db; + R_TRY(ncm::OpenContentMetaDatabase(std::addressof(db), ncm::StorageId::BuiltInSystem)); - NcmContentMetaKey *records = reinterpret_cast(work_buffer); + ncm::ContentMetaKey *keys = reinterpret_cast(work_buffer); + const auto content_meta_type = GetContentMetaType(mode); - const auto content_meta_type = GetNcmContentMetaType(mode); - s32 written_entries; - s32 total_entries; - R_TRY(ncmContentMetaDatabaseList(&meta_db, &total_entries, &written_entries, records, MaxContentMetas * sizeof(*records), content_meta_type, 0, 0, UINT64_MAX, NcmContentInstallType_Full)); - if (total_entries <= 0) { - return ResultBootImagePackageNotFound(); - } - - AMS_ABORT_UNLESS(total_entries == written_entries); + auto count = db.ListContentMeta(keys, MaxContentMetas, content_meta_type); + R_UNLESS(count.total > 0, ResultBootImagePackageNotFound()); /* Output is sorted, return the lowest valid exfat entry. */ - if (total_entries > 1) { - for (size_t i = 0; i < size_t(total_entries); i++) { + if (count.total > 1) { + for (auto i = 0; i < count.total; i++) { u8 attr; - R_TRY(ncmContentMetaDatabaseGetAttributes(&meta_db, &records[i], &attr)); + R_TRY(db.GetAttributes(std::addressof(attr), keys[i])); - if (attr & NcmContentMetaAttribute_IncludesExFatDriver) { - *out_data_id = records[i].id; + if (attr & ncm::ContentMetaAttribute_IncludesExFatDriver) { + out_data_id->value = keys[i].id; return ResultSuccess(); } } } /* If there's only one entry or no exfat entries, return that entry. */ - *out_data_id = records[0].id; + out_data_id->value = keys[0].id; return ResultSuccess(); } - Result VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + Result VerifyBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { switch (mode) { case BootModeType::Normal: return VerifyBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type); @@ -180,20 +174,22 @@ namespace ams::updater { } } - Result VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + Result VerifyBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { /* Ensure work buffer is big enough for us to do what we want to do. */ R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - R_TRY_CATCH(romfsMountFromDataArchive(data_id, NcmStorageId_BuiltInSystem, GetBootImagePackageMountPath())) { + /* Mount the boot image package. */ + const char *mount_name = GetMountName(); + R_TRY_CATCH(fs::MountSystemData(mount_name, data_id)) { R_CONVERT(fs::ResultTargetNotFound, ResultBootImagePackageNotFound()) } R_END_TRY_CATCH; - ON_SCOPE_EXIT { R_ABORT_UNLESS(romfsUnmount(GetBootImagePackageMountPath())); }; + ON_SCOPE_EXIT { fs::Unmount(mount_name); }; /* Read and validate hashes of boot images. */ { size_t size; - u8 nand_hash[SHA256_HASH_SIZE]; - u8 file_hash[SHA256_HASH_SIZE]; + u8 nand_hash[crypto::Sha256Generator::HashSize]; + u8 file_hash[crypto::Sha256Generator::HashSize]; Boot0Accessor boot0_accessor; R_TRY(boot0_accessor.Initialize()); @@ -209,44 +205,42 @@ namespace ams::updater { /* Compare Package1 Normal/Sub hashes. */ R_TRY(GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); + R_TRY(boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); /* Compare Package2 Normal/Sub hashes. */ R_TRY(GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); } return ResultSuccess(); } - Result VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + Result VerifyBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { /* Ensure work buffer is big enough for us to do what we want to do. */ R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - R_TRY_CATCH(romfsMountFromDataArchive(data_id, NcmStorageId_BuiltInSystem, GetBootImagePackageMountPath())) { + /* Mount the boot image package. */ + const char *mount_name = GetMountName(); + R_TRY_CATCH(fs::MountSystemData(mount_name, data_id)) { R_CONVERT(fs::ResultTargetNotFound, ResultBootImagePackageNotFound()) } R_END_TRY_CATCH; - ON_SCOPE_EXIT { R_ABORT_UNLESS(romfsUnmount(GetBootImagePackageMountPath())); }; + ON_SCOPE_EXIT { fs::Unmount(mount_name); }; /* Read and validate hashes of boot images. */ { size_t size; - u8 nand_hash[SHA256_HASH_SIZE]; - u8 file_hash[SHA256_HASH_SIZE]; + u8 nand_hash[crypto::Sha256Generator::HashSize]; + u8 file_hash[crypto::Sha256Generator::HashSize]; Boot0Accessor boot0_accessor; R_TRY(boot0_accessor.Initialize()); @@ -267,31 +261,27 @@ namespace ams::updater { /* Compare Package1 Normal/Sub hashes. */ R_TRY(GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); + R_TRY(boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); /* Compare Package2 Normal/Sub hashes. */ R_TRY(GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } + R_TRY(CompareHash(file_hash, nand_hash, sizeof(file_hash))); } return ResultSuccess(); } - Result UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + Result UpdateBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { switch (mode) { case BootModeType::Normal: return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type); @@ -301,14 +291,16 @@ namespace ams::updater { } } - Result UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + Result UpdateBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { /* Ensure work buffer is big enough for us to do what we want to do. */ R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - R_TRY_CATCH(romfsMountFromDataArchive(data_id, NcmStorageId_BuiltInSystem, GetBootImagePackageMountPath())) { + /* Mount the boot image package. */ + const char *mount_name = GetMountName(); + R_TRY_CATCH(fs::MountSystemData(mount_name, data_id)) { R_CONVERT(fs::ResultTargetNotFound, ResultBootImagePackageNotFound()) } R_END_TRY_CATCH; - ON_SCOPE_EXIT { R_ABORT_UNLESS(romfsUnmount(GetBootImagePackageMountPath())); }; + ON_SCOPE_EXIT { fs::Unmount(mount_name); }; { Boot0Accessor boot0_accessor; @@ -356,14 +348,16 @@ namespace ams::updater { return ResultSuccess(); } - Result UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + Result UpdateBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { /* Ensure work buffer is big enough for us to do what we want to do. */ R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - R_TRY_CATCH(romfsMountFromDataArchive(data_id, NcmStorageId_BuiltInSystem, GetBootImagePackageMountPath())) { + /* Mount the boot image package. */ + const char *mount_name = GetMountName(); + R_TRY_CATCH(fs::MountSystemData(mount_name, data_id)) { R_CONVERT(fs::ResultTargetNotFound, ResultBootImagePackageNotFound()) } R_END_TRY_CATCH; - ON_SCOPE_EXIT { R_ABORT_UNLESS(romfsUnmount(GetBootImagePackageMountPath())); }; + ON_SCOPE_EXIT { fs::Unmount(mount_name); }; { Boot0Accessor boot0_accessor; @@ -450,14 +444,10 @@ namespace ams::updater { R_TRY(accessor.PreserveAutoRcm(bct, work, which)); } - u8 file_hash[SHA256_HASH_SIZE]; - sha256CalculateHash(file_hash, bct, BctSize); + u8 file_hash[crypto::Sha256Generator::HashSize]; + crypto::GenerateSha256Hash(file_hash, sizeof(file_hash), bct, BctSize); - if (std::memcmp(file_hash, stored_hash, SHA256_HASH_SIZE) != 0) { - return ResultNeedsRepairBootImages(); - } - - return ResultSuccess(); + return CompareHash(file_hash, stored_hash, sizeof(file_hash)); } Result GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which) { @@ -476,6 +466,11 @@ namespace ams::updater { return accessor.Write(GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size, Package2Partition::Package2); } + Result CompareHash(const void *lhs, const void *rhs, size_t size) { + R_UNLESS(crypto::IsSameBytes(lhs, rhs, size), ResultNeedsRepairBootImages()); + return ResultSuccess(); + } + } BootImageUpdateType GetBootImageUpdateType(spl::HardwareType hw_type) { @@ -508,7 +503,7 @@ namespace ams::updater { } /* Get a session to ncm. */ - sm::ScopedServiceHolder ncm_holder; + sm::ScopedServiceHolder ncm_holder; R_ABORT_UNLESS(ncm_holder.GetResult()); /* Verify normal, verify safe as needed. */ diff --git a/libraries/libstratosphere/source/updater/updater_bis_management.cpp b/libraries/libstratosphere/source/updater/updater_bis_management.cpp index 709396aca..9e5879918 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_management.cpp +++ b/libraries/libstratosphere/source/updater/updater_bis_management.cpp @@ -18,47 +18,38 @@ namespace ams::updater { Result BisAccessor::Initialize() { - R_TRY(fsOpenBisStorage(&this->storage, this->partition_id)); - this->active = true; - return ResultSuccess(); + return fs::OpenBisPartition(std::addressof(this->storage), this->partition_id); } void BisAccessor::Finalize() { - if (this->active) { - fsStorageClose(&this->storage); - this->active = false; - } + /* ... */ } Result BisAccessor::Read(void *dst, size_t size, u64 offset) { AMS_ABORT_UNLESS((offset % SectorAlignment) == 0); - return fsStorageRead(&this->storage, offset, dst, size); + return this->storage->Read(static_cast(offset), dst, size); } Result BisAccessor::Write(u64 offset, const void *src, size_t size) { AMS_ABORT_UNLESS((offset % SectorAlignment) == 0); - return fsStorageWrite(&this->storage, offset, src, size); + return this->storage->Write(static_cast(offset), src, size); } Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) { AMS_ABORT_UNLESS((offset % SectorAlignment) == 0); AMS_ABORT_UNLESS((work_buffer_size % SectorAlignment) == 0); - FILE *bip_fp = fopen(bip_path, "rb"); - if (bip_fp == NULL) { - return ResultInvalidBootImagePackage(); - } - ON_SCOPE_EXIT { fclose(bip_fp); }; + fs::FileHandle file; + R_TRY_CATCH(fs::OpenFile(std::addressof(file), bip_path, fs::OpenMode_Read)) { + R_CONVERT(fs::ResultPathNotFound, ResultInvalidBootImagePackage()) + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { fs::CloseFile(file); }; size_t written = 0; while (true) { std::memset(work_buffer, 0, work_buffer_size); - size_t read_size = fread(work_buffer, 1, work_buffer_size, bip_fp); - if (read_size != work_buffer_size) { - if (ferror(bip_fp)) { - return fsdevGetLastResult(); - } - } + size_t read_size; + R_TRY(fs::ReadFile(std::addressof(read_size), file, written, work_buffer, work_buffer_size, fs::ReadOption())); AMS_ABORT_UNLESS(written + read_size <= size); size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment; @@ -91,18 +82,19 @@ namespace ams::updater { AMS_ABORT_UNLESS((offset % SectorAlignment) == 0); AMS_ABORT_UNLESS((work_buffer_size % SectorAlignment) == 0); - Sha256Context sha_ctx; - sha256ContextCreate(&sha_ctx); + crypto::Sha256Generator generator; + generator.Initialize(); size_t total_read = 0; while (total_read < hash_size) { size_t cur_read_size = std::min(work_buffer_size, size - total_read); size_t cur_update_size = std::min(cur_read_size, hash_size - total_read); R_TRY(this->Read(work_buffer, cur_read_size, offset + total_read)); - sha256ContextUpdate(&sha_ctx, work_buffer, cur_update_size); + generator.Update(work_buffer, cur_update_size); + total_read += cur_read_size; } - sha256ContextGetHash(&sha_ctx, dst); + generator.GetHash(dst, hash_size); return ResultSuccess(); } @@ -140,9 +132,10 @@ namespace ams::updater { size_t read_size; R_TRY(this->Read(&read_size, work_buffer, BctSize, which)); - void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffset); + void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffset); void *src_pubk = reinterpret_cast(reinterpret_cast(work_buffer) + BctPubkOffset); std::memcpy(dst_pubk, src_pubk, BctPubkSize); + return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/updater/updater_bis_management.hpp b/libraries/libstratosphere/source/updater/updater_bis_management.hpp index d0fdbd25d..c98333f6e 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_management.hpp +++ b/libraries/libstratosphere/source/updater/updater_bis_management.hpp @@ -20,19 +20,14 @@ namespace ams::updater { class BisAccessor { + NON_COPYABLE(BisAccessor); public: static constexpr size_t SectorAlignment = 0x200; private: - FsStorage storage = {}; - FsBisPartitionId partition_id; - bool active; + std::unique_ptr storage; + const fs::BisPartitionId partition_id; public: - BisAccessor(FsBisPartitionId id) : partition_id(id), active(false) { } - ~BisAccessor() { - if (this->active) { - fsStorageClose(&storage); - } - } + explicit BisAccessor(fs::BisPartitionId id) : partition_id(id) { /* ... */ } public: Result Initialize(); @@ -79,7 +74,7 @@ namespace ams::updater { }; struct Boot0Meta { - using EnumType = Boot0Partition; + using EnumType = Boot0Partition; using OffsetSizeType = OffsetSizeEntry; static constexpr size_t NumEntries = static_cast(EnumType::Count); @@ -96,7 +91,7 @@ namespace ams::updater { }; struct Boot1Meta { - using EnumType = Boot1Partition; + using EnumType = Boot1Partition; using OffsetSizeType = OffsetSizeEntry; static constexpr size_t NumEntries = static_cast(EnumType::Count); @@ -109,7 +104,7 @@ namespace ams::updater { }; struct Package2Meta { - using EnumType = Package2Partition; + using EnumType = Package2Partition; using OffsetSizeType = OffsetSizeEntry; static constexpr size_t NumEntries = static_cast(EnumType::Count); @@ -121,11 +116,12 @@ namespace ams::updater { template class PartitionAccessor : public BisAccessor { + NON_COPYABLE(PartitionAccessor); public: - using EnumType = typename Meta::EnumType; + using EnumType = typename Meta::EnumType; using OffsetSizeType = typename Meta::OffsetSizeType; public: - PartitionAccessor(FsBisPartitionId id) : BisAccessor(id) { } + explicit PartitionAccessor(fs::BisPartitionId id) : BisAccessor(id) { /* ... */ } private: constexpr const OffsetSizeType *FindEntry(EnumType which) { const OffsetSizeType *entry = nullptr; @@ -182,27 +178,27 @@ namespace ams::updater { RepairSub, }; - static constexpr FsBisPartitionId GetPackage2StorageId(Package2Type which) { + static constexpr fs::BisPartitionId GetPackage2StorageId(Package2Type which) { switch (which) { case Package2Type::NormalMain: - return FsBisPartitionId_BootConfigAndPackage2Part1; + return fs::BisPartitionId::BootConfigAndPackage2Part1; case Package2Type::NormalSub: - return FsBisPartitionId_BootConfigAndPackage2Part2; + return fs::BisPartitionId::BootConfigAndPackage2Part2; case Package2Type::SafeMain: - return FsBisPartitionId_BootConfigAndPackage2Part3; + return fs::BisPartitionId::BootConfigAndPackage2Part3; case Package2Type::SafeSub: - return FsBisPartitionId_BootConfigAndPackage2Part4; + return fs::BisPartitionId::BootConfigAndPackage2Part4; case Package2Type::RepairMain: - return FsBisPartitionId_BootConfigAndPackage2Part5; + return fs::BisPartitionId::BootConfigAndPackage2Part5; case Package2Type::RepairSub: - return FsBisPartitionId_BootConfigAndPackage2Part6; + return fs::BisPartitionId::BootConfigAndPackage2Part6; AMS_UNREACHABLE_DEFAULT_CASE(); } } class Boot0Accessor : public PartitionAccessor { public: - static constexpr FsBisPartitionId PartitionId = FsBisPartitionId_BootPartition1Root; + static constexpr fs::BisPartitionId PartitionId = fs::BisPartitionId::BootPartition1Root; static constexpr size_t BctPubkOffset = 0x210; static constexpr size_t BctPubkSize = 0x100; static constexpr size_t BctEksOffset = 0x450; @@ -222,7 +218,7 @@ namespace ams::updater { class Boot1Accessor : public PartitionAccessor { public: - static constexpr FsBisPartitionId PartitionId = FsBisPartitionId_BootPartition2Root; + static constexpr fs::BisPartitionId PartitionId = fs::BisPartitionId::BootPartition2Root; public: Boot1Accessor() : PartitionAccessor(PartitionId) { } }; diff --git a/libraries/libstratosphere/source/updater/updater_files.cpp b/libraries/libstratosphere/source/updater/updater_files.cpp index e523fb3de..3266862f9 100644 --- a/libraries/libstratosphere/source/updater/updater_files.cpp +++ b/libraries/libstratosphere/source/updater/updater_files.cpp @@ -18,49 +18,43 @@ namespace ams::updater { Result ReadFile(size_t *out_size, void *dst, size_t dst_size, const char *path) { - FILE *fp = fopen(path, "rb"); - if (fp == NULL) { - return ResultInvalidBootImagePackage(); - } - ON_SCOPE_EXIT { fclose(fp); }; + /* Open the file. */ + fs::FileHandle file; + R_TRY_CATCH(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read)) { + R_CONVERT(fs::ResultPathNotFound, ResultInvalidBootImagePackage()) + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { fs::CloseFile(file); }; std::memset(dst, 0, dst_size); - size_t read_size = fread(dst, 1, dst_size, fp); - if (ferror(fp)) { - return fsdevGetLastResult(); - } - *out_size = read_size; - return ResultSuccess(); + return fs::ReadFile(out_size, file, 0, dst, dst_size, fs::ReadOption()); } Result GetFileHash(size_t *out_size, void *dst_hash, const char *path, void *work_buffer, size_t work_buffer_size) { - FILE *fp = fopen(path, "rb"); - if (fp == NULL) { - return ResultInvalidBootImagePackage(); - } - ON_SCOPE_EXIT { fclose(fp); }; + /* Open the file. */ + fs::FileHandle file; + R_TRY_CATCH(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read)) { + R_CONVERT(fs::ResultPathNotFound, ResultInvalidBootImagePackage()) + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { fs::CloseFile(file); }; - Sha256Context sha_ctx; - sha256ContextCreate(&sha_ctx); + /* Read in chunks, hashing as we go. */ + crypto::Sha256Generator generator; + generator.Initialize(); size_t total_size = 0; while (true) { - size_t read_size = fread(work_buffer, 1, work_buffer_size, fp); - if (ferror(fp)) { - return fsdevGetLastResult(); - } - if (read_size == 0) { - break; - } + size_t size; + R_TRY(fs::ReadFile(std::addressof(size), file, total_size, work_buffer, work_buffer_size, fs::ReadOption())); - sha256ContextUpdate(&sha_ctx, work_buffer, read_size); - total_size += read_size; - if (read_size != work_buffer_size) { + generator.Update(work_buffer, size); + total_size += size; + + if (size != work_buffer_size) { break; } } - sha256ContextGetHash(&sha_ctx, dst_hash); + generator.GetHash(dst_hash, crypto::Sha256Generator::HashSize); *out_size = total_size; return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/updater/updater_paths.cpp b/libraries/libstratosphere/source/updater/updater_paths.cpp index f6a436d5d..6ede7d00d 100644 --- a/libraries/libstratosphere/source/updater/updater_paths.cpp +++ b/libraries/libstratosphere/source/updater/updater_paths.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include #include "updater_paths.hpp" namespace ams::updater { @@ -21,7 +20,7 @@ namespace ams::updater { namespace { /* Actual paths. */ - constexpr const char *BootImagePackageMountPath = "bip"; + constexpr const char *BootImagePackageMountName = "bip"; constexpr const char *BctPathNx = "bip:/nx/bct"; constexpr const char *Package1PathNx = "bip:/nx/package1"; constexpr const char *Package2PathNx = "bip:/nx/package2"; @@ -33,12 +32,12 @@ namespace ams::updater { AMS_ABORT_UNLESS(num_candidates > 0); for (size_t i = 0; i < num_candidates; i++) { - struct stat buf; - if (stat(candidates[i], &buf) != 0) { + fs::DirectoryEntryType type; + if (R_FAILED(fs::GetEntryType(std::addressof(type), candidates[i]))) { continue; } - if (!S_ISREG(buf.st_mode)) { + if (type != fs::DirectoryEntryType_File) { continue; } @@ -51,8 +50,8 @@ namespace ams::updater { } - const char *GetBootImagePackageMountPath() { - return BootImagePackageMountPath; + const char *GetMountName() { + return BootImagePackageMountName; } diff --git a/libraries/libstratosphere/source/updater/updater_paths.hpp b/libraries/libstratosphere/source/updater/updater_paths.hpp index 0627280f6..f6f5c4c2a 100644 --- a/libraries/libstratosphere/source/updater/updater_paths.hpp +++ b/libraries/libstratosphere/source/updater/updater_paths.hpp @@ -19,7 +19,7 @@ namespace ams::updater { /* Path functionality. */ - const char *GetBootImagePackageMountPath(); + const char *GetMountName(); const char *GetBctPath(BootImageUpdateType boot_image_update_type); const char *GetPackage1Path(BootImageUpdateType boot_image_update_type); const char *GetPackage2Path(BootImageUpdateType boot_image_update_type); diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index a731c5041..7af09fc21 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -44,26 +44,158 @@ namespace ams::fs { R_DEFINE_ERROR_RANGE(GameCardAccessFailed, 2500, 2999); - R_DEFINE_ERROR_RESULT(NotImplemented, 3001); - R_DEFINE_ERROR_RESULT(OutOfRange, 3005); + R_DEFINE_ERROR_RESULT(NotImplemented, 3001); + R_DEFINE_ERROR_RESULT(UnsupportedVersion, 3002); + R_DEFINE_ERROR_RESULT(OutOfRange, 3005); + + R_DEFINE_ERROR_RESULT(SystemPartitionNotReady, 3100); R_DEFINE_ERROR_RANGE(AllocationFailure, 3200, 3499); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorA, 3211); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorB, 3212); + R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215); + R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216); + R_DEFINE_ERROR_RESULT(AllocationFailureInBisC, 3217); + R_DEFINE_ERROR_RESULT(AllocationFailureInCodeA, 3218); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentA, 3219); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221); + R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222); + R_DEFINE_ERROR_RESULT(AllocationFailureInDataB, 3223); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardA, 3225); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardD, 3228); + R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244); + R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245); + R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249); R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352); R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355); + R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365); + R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterB, 3366); R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367); + R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377); R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407); + R_DEFINE_ERROR_RESULT(AllocationFailureInNew, 3420); R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999); R_DEFINE_ERROR_RANGE(DataCorrupted, 4000, 4999); R_DEFINE_ERROR_RANGE(RomCorrupted, 4001, 4299); + R_DEFINE_ERROR_RESULT(UnsupportedRomVersion, 4002); + + R_DEFINE_ERROR_RANGE(RomNcaCorrupted, 4041, 4139); + R_DEFINE_ERROR_RANGE(RomNcaFileSystemCorrupted, 4051, 4069); + R_DEFINE_ERROR_RESULT(InvalidRomNcaFileSystemType, 4052); + R_DEFINE_ERROR_RESULT(InvalidRomAcidFileSize, 4053); + R_DEFINE_ERROR_RESULT(InvalidRomAcidSize, 4054); + R_DEFINE_ERROR_RESULT(InvalidRomAcid, 4055); + R_DEFINE_ERROR_RESULT(RomAcidVerificationFailed, 4056); + R_DEFINE_ERROR_RESULT(InvalidRomNcaSignature, 4057); + R_DEFINE_ERROR_RESULT(RomNcaHeaderSignature1VerificationFailed, 4058); + R_DEFINE_ERROR_RESULT(RomNcaHeaderSignature2VerificationFailed, 4059); + R_DEFINE_ERROR_RESULT(RomNcaFsHeaderHashVerificationFailed, 4060); + R_DEFINE_ERROR_RESULT(InvalidRomNcaKeyIndex, 4061); + R_DEFINE_ERROR_RESULT(InvalidRomNcaFsHeaderHashType, 4062); + R_DEFINE_ERROR_RESULT(InvalidRomNcaFsHeaderEncryptionType, 4063); + + R_DEFINE_ERROR_RANGE(RomNcaHierarchicalSha256StorageCorrupted, 4071, 4079); + R_DEFINE_ERROR_RESULT(InvalidRomHierarchicalSha256BlockSize, 4072); + R_DEFINE_ERROR_RESULT(InvalidRomHierarchicalSha256LayerCount, 4073); + R_DEFINE_ERROR_RESULT(RomHierarchicalSha256BaseStorageTooLarge, 4074); + R_DEFINE_ERROR_RESULT(RomHierarchicalSha256HashVerificationFailed, 4075); + + R_DEFINE_ERROR_RANGE(RomIntegrityVerificationStorageCorrupted, 4141, 4179); + R_DEFINE_ERROR_RESULT(IncorrectRomIntegrityVerificationMagic, 4142); + R_DEFINE_ERROR_RESULT(InvalidRomZeroHash, 4143); + R_DEFINE_ERROR_RESULT(RomNonRealDataVerificationFailed, 4144); + R_DEFINE_ERROR_RESULT(InvalidRomHierarchicalIntegrityVerificationLayerCount, 4145); + + R_DEFINE_ERROR_RANGE(RomRealDataVerificationFailed, 4151, 4159); + R_DEFINE_ERROR_RESULT(ClearedRomRealDataVerificationFailed, 4152); + R_DEFINE_ERROR_RESULT(UnclearedRomRealDataVerificationFailed, 4153); + + R_DEFINE_ERROR_RANGE(RomPartitionFileSystemCorrupted, 4181, 4199); + R_DEFINE_ERROR_RESULT(InvalidRomSha256PartitionHashTarget, 4182); + R_DEFINE_ERROR_RESULT(RomSha256PartitionHashVerificationFailed, 4183); + R_DEFINE_ERROR_RESULT(RomPartitionSignatureVerificationFailed, 4184); + R_DEFINE_ERROR_RESULT(RomSha256PartitionSignatureVerificationFailed, 4185); + R_DEFINE_ERROR_RESULT(InvalidRomPartitionEntryOffset, 4186); + R_DEFINE_ERROR_RESULT(InvalidRomSha256PartitionMetaDataSize, 4187); + + R_DEFINE_ERROR_RANGE(RomBuiltInStorageCorrupted, 4201, 4219); + R_DEFINE_ERROR_RESULT(RomGptHeaderVerificationFailed, 4202); + + R_DEFINE_ERROR_RANGE(RomHostFileSystemCorrupted, 4241, 4259); + R_DEFINE_ERROR_RESULT(RomHostEntryCorrupted, 4242); + R_DEFINE_ERROR_RESULT(RomHostFileDataCorrupted, 4243); + R_DEFINE_ERROR_RESULT(RomHostFileCorrupted, 4244); + R_DEFINE_ERROR_RESULT(InvalidRomHostHandle, 4245); + + R_DEFINE_ERROR_RANGE(RomDatabaseCorrupted, 4261, 4279); + R_DEFINE_ERROR_RESULT(InvalidRomAllocationTableBlock, 4262); + R_DEFINE_ERROR_RESULT(InvalidRomKeyValueListElementIndex, 4263); + R_DEFINE_ERROR_RANGE(SaveDataCorrupted, 4301, 4499); R_DEFINE_ERROR_RANGE(NcaCorrupted, 4501, 4599); + R_DEFINE_ERROR_RANGE(NcaFileSystemCorrupted, 4511, 4529); + R_DEFINE_ERROR_RESULT(InvalidNcaFileSystemType, 4512); + R_DEFINE_ERROR_RESULT(InvalidAcidFileSize, 4513); + R_DEFINE_ERROR_RESULT(InvalidAcidSize, 4514); + R_DEFINE_ERROR_RESULT(InvalidAcid, 4515); + R_DEFINE_ERROR_RESULT(AcidVerificationFailed, 4516); + R_DEFINE_ERROR_RESULT(InvalidNcaSignature, 4517); + R_DEFINE_ERROR_RESULT(NcaHeaderSignature1VerificationFailed, 4518); + R_DEFINE_ERROR_RESULT(NcaHeaderSignature2VerificationFailed, 4519); + R_DEFINE_ERROR_RESULT(NcaFsHeaderHashVerificationFailed, 4520); + R_DEFINE_ERROR_RESULT(InvalidNcaKeyIndex, 4521); + R_DEFINE_ERROR_RESULT(InvalidNcaFsHeaderHashType, 4522); + R_DEFINE_ERROR_RESULT(InvalidNcaFsHeaderEncryptionType, 4523); + + R_DEFINE_ERROR_RANGE(NcaHierarchicalSha256StorageCorrupted, 4531, 4539); + R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256BlockSize, 4532); + R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256LayerCount, 4533); + R_DEFINE_ERROR_RESULT(HierarchicalSha256BaseStorageTooLarge, 4534); + R_DEFINE_ERROR_RESULT(HierarchicalSha256HashVerificationFailed, 4535); + R_DEFINE_ERROR_RANGE(IntegrityVerificationStorageCorrupted, 4601, 4639); + R_DEFINE_ERROR_RESULT(IncorrectIntegrityVerificationMagic, 4602); + R_DEFINE_ERROR_RESULT(InvalidZeroHash, 4603); + R_DEFINE_ERROR_RESULT(NonRealDataVerificationFailed, 4604); + R_DEFINE_ERROR_RESULT(InvalidHierarchicalIntegrityVerificationLayerCount, 4605); + + R_DEFINE_ERROR_RANGE(RealDataVerificationFailed, 4611, 4619); + R_DEFINE_ERROR_RESULT(ClearedRealDataVerificationFailed, 4612); + R_DEFINE_ERROR_RESULT(UnclearedRealDataVerificationFailed, 4613); + R_DEFINE_ERROR_RANGE(PartitionFileSystemCorrupted, 4641, 4659); + R_DEFINE_ERROR_RESULT(InvalidSha256PartitionHashTarget, 4642); + R_DEFINE_ERROR_RESULT(Sha256PartitionHashVerificationFailed, 4643); + R_DEFINE_ERROR_RESULT(PartitionSignatureVerificationFailed, 4644); + R_DEFINE_ERROR_RESULT(Sha256PartitionSignatureVerificationFailed, 4645); + R_DEFINE_ERROR_RESULT(InvalidPartitionEntryOffset, 4646); + R_DEFINE_ERROR_RESULT(InvalidSha256PartitionMetaDataSize, 4647); + R_DEFINE_ERROR_RANGE(BuiltInStorageCorrupted, 4661, 4679); + R_DEFINE_ERROR_RESULT(GptHeaderVerificationFailed, 4662); + + R_DEFINE_ERROR_RANGE(FatFileSystemCorrupted, 4681, 4699); + R_DEFINE_ERROR_RANGE(HostFileSystemCorrupted, 4701, 4719); + R_DEFINE_ERROR_RESULT(HostEntryCorrupted, 4702); + R_DEFINE_ERROR_RESULT(HostFileDataCorrupted, 4703); + R_DEFINE_ERROR_RESULT(HostFileCorrupted, 4704); + R_DEFINE_ERROR_RESULT(InvalidHostHandle, 4705); + R_DEFINE_ERROR_RANGE(DatabaseCorrupted, 4721, 4739); + R_DEFINE_ERROR_RESULT(InvalidAllocationTableBlock, 4722); + R_DEFINE_ERROR_RESULT(InvalidKeyValueListElementIndex, 4723); + R_DEFINE_ERROR_RANGE(AesXtsFileSystemCorrupted, 4741, 4759); R_DEFINE_ERROR_RANGE(SaveDataTransferDataCorrupted, 4761, 4769); R_DEFINE_ERROR_RANGE(SignedSystemPartitionDataCorrupted, 4771, 4779); @@ -71,6 +203,7 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(GameCardLogoDataCorrupted, 4781); R_DEFINE_ERROR_RANGE(Unexpected, 5000, 5999); + R_DEFINE_ERROR_RESULT(UnexpectedInFindFileSystemA, 5319); R_DEFINE_ERROR_RANGE(PreconditionViolation, 6000, 6499); R_DEFINE_ERROR_RANGE(InvalidArgument, 6001, 6199); @@ -104,13 +237,31 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201); R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368); R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449); + R_DEFINE_ERROR_RESULT(NeedFlush, 6454); + R_DEFINE_ERROR_RESULT(FileNotClosed, 6455); + R_DEFINE_ERROR_RESULT(DirectoryNotClosed, 6456); R_DEFINE_ERROR_RESULT(WriteModeFileNotClosed, 6457); + R_DEFINE_ERROR_RESULT(AllocatorAlreadyRegistered, 6458); + R_DEFINE_ERROR_RESULT(DefaultAllocatorUsed, 6459); R_DEFINE_ERROR_RESULT(AllocatorAlignmentViolation, 6461); R_DEFINE_ERROR_RESULT(UserNotExist, 6465); + R_DEFINE_ERROR_RANGE(NotFound, 6600, 6699); + R_DEFINE_ERROR_RANGE(OutOfResource, 6700, 6799); R_DEFINE_ERROR_RESULT(MappingTableFull, 6706); R_DEFINE_ERROR_RESULT(OpenCountLimit, 6709); @@ -119,6 +270,27 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(MapFull, 6811); R_DEFINE_ERROR_RANGE(BadState, 6900, 6999); - R_DEFINE_ERROR_RESULT(NotMounted, 6905); + R_DEFINE_ERROR_RESULT(NotInitialized, 6902); + R_DEFINE_ERROR_RESULT(NotMounted, 6905); + + + R_DEFINE_ERROR_RANGE(DbmNotFound, 7901, 7904); + R_DEFINE_ERROR_RESULT(DbmKeyNotFound, 7902); + R_DEFINE_ERROR_RESULT(DbmFileNotFound, 7903); + R_DEFINE_ERROR_RESULT(DbmDirectoryNotFound, 7904); + + R_DEFINE_ERROR_RESULT(DbmAlreadyExists, 7906); + R_DEFINE_ERROR_RESULT(DbmKeyFull, 7907); + R_DEFINE_ERROR_RESULT(DbmDirectoryEntryFull, 7908); + R_DEFINE_ERROR_RESULT(DbmFileEntryFull, 7909); + + R_DEFINE_ERROR_RANGE(DbmFindFinished, 7910, 7912); + R_DEFINE_ERROR_RESULT(DbmFindKeyFinished, 7911); + R_DEFINE_ERROR_RESULT(DbmIterationFinished, 7912); + + R_DEFINE_ERROR_RESULT(DbmInvalidOperation, 7914); + R_DEFINE_ERROR_RESULT(DbmInvalidPathFormat, 7915); + R_DEFINE_ERROR_RESULT(DbmDirectoryNameTooLong, 7916); + R_DEFINE_ERROR_RESULT(DbmFileNameTooLong, 7917); } diff --git a/libraries/libvapours/include/vapours/results/lr_results.hpp b/libraries/libvapours/include/vapours/results/lr_results.hpp index 693d6a9e6..7458730f6 100644 --- a/libraries/libvapours/include/vapours/results/lr_results.hpp +++ b/libraries/libvapours/include/vapours/results/lr_results.hpp @@ -28,6 +28,7 @@ namespace ams::lr { R_DEFINE_ERROR_RESULT(AddOnContentNotFound, 7); R_DEFINE_ERROR_RESULT(ControlNotFound, 8); R_DEFINE_ERROR_RESULT(LegalInformationNotFound, 9); + R_DEFINE_ERROR_RESULT(DebugProgramNotFound, 10); R_DEFINE_ERROR_RESULT(TooManyRegisteredPaths, 90); diff --git a/libraries/libvapours/include/vapours/results/ncm_results.hpp b/libraries/libvapours/include/vapours/results/ncm_results.hpp index 7148d0279..494d7b4bd 100644 --- a/libraries/libvapours/include/vapours/results/ncm_results.hpp +++ b/libraries/libvapours/include/vapours/results/ncm_results.hpp @@ -21,6 +21,7 @@ namespace ams::ncm { R_DEFINE_NAMESPACE_RESULT_MODULE(5); + R_DEFINE_ERROR_RESULT(InvalidContentStorageBase, 1); R_DEFINE_ERROR_RESULT(PlaceHolderAlreadyExists, 2); R_DEFINE_ERROR_RESULT(PlaceHolderNotFound, 3); R_DEFINE_ERROR_RESULT(ContentAlreadyExists, 4); @@ -32,22 +33,28 @@ namespace ams::ncm { R_DEFINE_ERROR_RESULT(InvalidContentStorage, 100); R_DEFINE_ERROR_RESULT(InvalidContentMetaDatabase, 110); + R_DEFINE_ERROR_RESULT(InvalidPackageFormat, 130); + + R_DEFINE_ERROR_RESULT(InvalidPlaceHolderFile, 170); R_DEFINE_ERROR_RESULT(BufferInsufficient, 180); + R_DEFINE_ERROR_RESULT(WriteToReadOnlyContentStorage, 190); R_DEFINE_ERROR_RESULT(InvalidContentMetaKey, 240); + R_DEFINE_ERROR_RESULT(ContentStorageBaseNotFound, 310); + R_DEFINE_ERROR_RANGE(ContentStorageNotActive, 250, 258); - R_DEFINE_ERROR_RESULT(GameCardContentStorageNotActive, 251); - R_DEFINE_ERROR_RESULT(NandSystemContentStorageNotActive, 252); - R_DEFINE_ERROR_RESULT(NandUserContentStorageNotActive, 253); - R_DEFINE_ERROR_RESULT(SdCardContentStorageNotActive, 254); - R_DEFINE_ERROR_RESULT(UnknownContentStorageNotActive, 258); + R_DEFINE_ERROR_RESULT(GameCardContentStorageNotActive, 251); + R_DEFINE_ERROR_RESULT(BuiltInSystemContentStorageNotActive, 252); + R_DEFINE_ERROR_RESULT(BuiltInUserContentStorageNotActive, 253); + R_DEFINE_ERROR_RESULT(SdCardContentStorageNotActive, 254); + R_DEFINE_ERROR_RESULT(UnknownContentStorageNotActive, 258); R_DEFINE_ERROR_RANGE(ContentMetaDatabaseNotActive, 260, 268); - R_DEFINE_ERROR_RESULT(GameCardContentMetaDatabaseNotActive, 261); - R_DEFINE_ERROR_RESULT(NandSystemContentMetaDatabaseNotActive, 262); - R_DEFINE_ERROR_RESULT(NandUserContentMetaDatabaseNotActive, 263); - R_DEFINE_ERROR_RESULT(SdCardContentMetaDatabaseNotActive, 264); - R_DEFINE_ERROR_RESULT(UnknownContentMetaDatabaseNotActive, 268); + R_DEFINE_ERROR_RESULT(GameCardContentMetaDatabaseNotActive, 261); + R_DEFINE_ERROR_RESULT(BuiltInSystemContentMetaDatabaseNotActive, 262); + R_DEFINE_ERROR_RESULT(BuiltInUserContentMetaDatabaseNotActive, 263); + R_DEFINE_ERROR_RESULT(SdCardContentMetaDatabaseNotActive, 264); + R_DEFINE_ERROR_RESULT(UnknownContentMetaDatabaseNotActive, 268); R_DEFINE_ERROR_RANGE(InvalidArgument, 8181, 8191); R_DEFINE_ERROR_RESULT(InvalidOffset, 8182); diff --git a/libraries/libvapours/include/vapours/results/results_common.hpp b/libraries/libvapours/include/vapours/results/results_common.hpp index 74517f274..1fb735f88 100644 --- a/libraries/libvapours/include/vapours/results/results_common.hpp +++ b/libraries/libvapours/include/vapours/results/results_common.hpp @@ -264,6 +264,9 @@ namespace ams { } \ }) +/// Evaluates a boolean expression, and succeeds if that expression is true. +#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess()) + /// Helpers for pattern-matching on a result expression, if the result would fail. #define R_CURRENT_RESULT _tmp_r_current_result diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index b1d000fea..f3dbdb62d 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -32,3 +32,4 @@ #include #include #include +#include diff --git a/libraries/libvapours/include/vapours/util/util_bounded_map.hpp b/libraries/libvapours/include/vapours/util/util_bounded_map.hpp new file mode 100644 index 000000000..d76f7a6d7 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_bounded_map.hpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::util { + + template + class BoundedMap { + private: + std::array, N> keys; + std::array values; + private: + ALWAYS_INLINE void FreeEntry(size_t i) { + this->keys[i].reset(); + GetReference(this->values[i]).~Value(); + } + public: + Value *Find(const Key &key) { + for (size_t i = 0; i < N; i++) { + if (this->keys[i] && this->keys[i].value() == key) { + return GetPointer(this->values[i]); + } + } + return nullptr; + } + + void Remove(const Key &key) { + for (size_t i = 0; i < N; i++) { + if (this->keys[i] && this->keys[i].value() == key) { + this->FreeEntry(i); + break; + } + } + } + + void RemoveAll() { + for (size_t i = 0; i < N; i++) { + this->FreeEntry(i); + } + } + + bool IsFull() { + for (size_t i = 0; i < N; i++) { + if (!this->keys[i]) { + return false; + } + } + + return true; + } + + bool Insert(const Key &key, Value &&value) { + /* We can't insert if the key is used. */ + if (this->Find(key) != nullptr) { + return false; + } + + /* Find a free value. */ + for (size_t i = 0; i < N; i++) { + if (!this->keys[i]) { + this->keys[i] = key; + new (GetPointer(this->values[i])) Value(std::move(value)); + return true; + } + } + + return false; + } + + bool InsertOrAssign(const Key &key, Value &&value) { + /* Try to find and assign an existing value. */ + for (size_t i = 0; i < N; i++) { + if (this->keys[i] && this->keys[i].value() == key) { + GetReference(this->values[i]) = std::move(value); + return true; + } + } + + /* Find a free value. */ + for (size_t i = 0; i < N; i++) { + if (!this->keys[i]) { + this->keys[i] = key; + new (GetPointer(this->values[i])) Value(std::move(value)); + return true; + } + } + + return false; + } + + template + bool Emplace(const Key &key, Args&&... args) { + /* We can't emplace if the key is used. */ + if (this->Find(key) != nullptr) { + return false; + } + + /* Find a free value. */ + for (size_t i = 0; i < N; i++) { + this->keys[i] = key; + if (!this->keys[i]) { + this->keys[i] = key; + new (GetPointer(this->values[i])) Value(std::forward(args)...); + return true; + } + } + + return false; + } + }; + +} diff --git a/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp b/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp index b19edb70f..511b289a7 100644 --- a/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp +++ b/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp @@ -276,7 +276,7 @@ namespace ams::util { splice_impl(pos, first, last); } - iterator erase(const iterator pos) { + iterator erase(const_iterator pos) { if (pos == this->end()) { return this->end(); } @@ -529,7 +529,7 @@ namespace ams::util { this->impl.splice(pos.GetImplIterator(), o.impl, first.GetImplIterator(), last.GetImplIterator()); } - iterator erase(const iterator pos) { + iterator erase(const_iterator pos) { return iterator(this->impl.erase(pos.GetImplIterator())); } diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 645bc1b7f..d70f63a3a 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt boot2 +MODULES := loader ncm pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt boot2 SUBFOLDERS := $(MODULES) diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index e2dd87f91..7e47169dd 100644 --- a/stratosphere/ams_mitm/source/amsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -40,7 +40,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::AtmosphereMitm; + ncm::ProgramId CurrentProgramId = ncm::AtmosphereProgramId::Mitm; namespace result { diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp index df104ff7d..b4bd1d4bd 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp @@ -31,8 +31,8 @@ namespace ams::mitm::bpc { * - fatal, to simplify payload reboot logic significantly * - hbl, to allow homebrew to take advantage of the feature. */ - return client_info.program_id == ncm::ProgramId::Am || - client_info.program_id == ncm::ProgramId::Fatal || + return client_info.program_id == ncm::SystemProgramId::Am || + client_info.program_id == ncm::SystemProgramId::Fatal || client_info.override_status.IsHbl(); } public: diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp index 158abadf5..71d3e544e 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -137,7 +137,7 @@ namespace ams::mitm::fs { Result OpenWebContentFileSystem(sf::Out> &out, ncm::ProgramId client_program_id, ncm::ProgramId program_id, FsFileSystemType filesystem_type, Service *fwd, const fssrv::sf::Path *path, bool with_id, bool try_program_specific) { /* Check first that we're a web applet opening web content. */ - R_UNLESS(ncm::IsWebAppletProgramId(client_program_id), sm::mitm::ResultShouldForwardToSession()); + R_UNLESS(ncm::IsWebAppletId(client_program_id), sm::mitm::ResultShouldForwardToSession()); R_UNLESS(filesystem_type == FsFileSystemType_ContentManual, sm::mitm::ResultShouldForwardToSession()); /* Try to mount the HBL web filesystem. If this succeeds then we're done. */ @@ -162,8 +162,8 @@ namespace ams::mitm::fs { Result FsMitmService::OpenSdCardFileSystem(sf::Out> out) { /* We only care about redirecting this for NS/emummc. */ - R_UNLESS(this->client_info.program_id == ncm::ProgramId::Ns, sm::mitm::ResultShouldForwardToSession()); - R_UNLESS(emummc::IsActive(), sm::mitm::ResultShouldForwardToSession()); + R_UNLESS(this->client_info.program_id == ncm::SystemProgramId::Ns, sm::mitm::ResultShouldForwardToSession()); + R_UNLESS(emummc::IsActive(), sm::mitm::ResultShouldForwardToSession()); /* Create a new SD card filesystem. */ FsFileSystem sd_fs; @@ -178,7 +178,7 @@ namespace ams::mitm::fs { Result FsMitmService::OpenSaveDataFileSystem(sf::Out> out, u8 _space_id, const FsSaveDataAttribute &attribute) { /* We only want to intercept saves for games, right now. */ - const bool is_game_or_hbl = this->client_info.override_status.IsHbl() || ncm::IsApplicationProgramId(this->client_info.program_id); + const bool is_game_or_hbl = this->client_info.override_status.IsHbl() || ncm::IsApplicationId(this->client_info.program_id); R_UNLESS(is_game_or_hbl, sm::mitm::ResultShouldForwardToSession()); /* Only redirect if the appropriate system setting is set. */ @@ -333,10 +333,13 @@ namespace ams::mitm::fs { return ResultSuccess(); } - Result FsMitmService::OpenDataStorageByDataId(sf::Out> out, ncm::ProgramId /* TODO: ncm::DataId */ data_id, u8 storage_id) { + Result FsMitmService::OpenDataStorageByDataId(sf::Out> out, ncm::DataId _data_id, u8 storage_id) { /* Only mitm if we should override contents for the current process. */ R_UNLESS(this->client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession()); + /* TODO: Decide how to handle DataId vs ProgramId for this API. */ + const ncm::ProgramId data_id = {_data_id.value}; + /* Only mitm if there is actually an override romfs. */ R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession()); diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp index 8367396e0..63ff73700 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp @@ -50,12 +50,12 @@ namespace ams::mitm::fs { } /* We want to mitm ns, to intercept SD card requests. */ - if (program_id == ncm::ProgramId::Ns) { + if (program_id == ncm::SystemProgramId::Ns) { return true; } /* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */ - if (program_id == ncm::ProgramId::Sdb) { + if (program_id == ncm::SystemProgramId::Sdb) { return true; } @@ -69,7 +69,7 @@ namespace ams::mitm::fs { /* Figure out why, and address it. */ /* TODO: This may be because pre-rewrite code really mismanaged domain objects in a way that would cause bad things. */ /* Need to verify if this is fixed now. */ - if (client_info.program_id == ncm::ProgramId::AppletQlaunch || client_info.program_id == ncm::ProgramId::AppletMaintenanceMenu) { + if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) { has_launched_qlaunch = true; } @@ -85,7 +85,7 @@ namespace ams::mitm::fs { Result OpenSaveDataFileSystem(sf::Out> out, u8 space_id, const FsSaveDataAttribute &attribute); Result OpenBisStorage(sf::Out> out, u32 bis_partition_id); Result OpenDataStorageByCurrentProcess(sf::Out> out); - Result OpenDataStorageByDataId(sf::Out> out, ncm::ProgramId /* TODO: ncm::DataId */ data_id, u8 storage_id); + Result OpenDataStorageByDataId(sf::Out> out, ncm::DataId data_id, u8 storage_id); public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(OpenFileSystemWithPatch, hos::Version_200), diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp index dee285d74..311a3d4e2 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp @@ -31,7 +31,7 @@ namespace ams::mitm::fs { /* RCM bug patched. */ /* Only allow NS to update the BCT pubks. */ /* AutoRCM on a patched unit will cause a brick, so homebrew should NOT be allowed to write. */ - return this->client_info.program_id == ncm::ProgramId::Ns; + return this->client_info.program_id == ncm::SystemProgramId::Ns; } else { /* RCM bug unpatched. */ /* Allow homebrew but not NS to update the BCT pubks. */ diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp index 0da76868d..a1ac52ed2 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp @@ -140,8 +140,7 @@ namespace ams::mitm::fs { private: bool CanModifyBctPublicKey(); public: - Boot0Storage(FsStorage *s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ } - Boot0Storage(FsStorage s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ } + Boot0Storage(FsStorage &s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ } public: virtual Result Read(s64 offset, void *_buffer, size_t size) override; virtual Result Write(s64 offset, const void *_buffer, size_t size) override; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp index ce3e4da42..8594045e9 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp @@ -98,7 +98,7 @@ namespace ams::mitm::fs { Result LayeredRomfsStorage::Read(s64 offset, void *buffer, size_t size) { /* Check if we can succeed immediately. */ R_UNLESS(size >= 0, fs::ResultInvalidSize()); - R_UNLESS(size > 0, ResultSuccess()); + R_SUCCEED_IF(size == 0); /* Ensure we're initialized. */ if (!this->is_initialized) { diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp index e928d8bcf..6782fa961 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp @@ -49,6 +49,16 @@ namespace ams::mitm::fs { virtual Result GetSize(s64 *out_size) override; virtual Result Flush() override; virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* TODO: Better result code? */ + return ams::fs::ResultUnsupportedOperation(); + } + + virtual Result SetSize(s64 size) override { + /* TODO: Better result code? */ + return ams::fs::ResultUnsupportedOperation(); + } }; } diff --git a/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.hpp b/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.hpp index 63ffde9f5..d01e7dcb6 100644 --- a/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.hpp @@ -30,7 +30,7 @@ namespace ams::mitm::ns { /* We will mitm: * - web applets, to facilitate hbl web browser launching. */ - return ncm::IsWebAppletProgramId(client_info.program_id); + return ncm::IsWebAppletId(client_info.program_id); } public: SF_MITM_SERVICE_OBJECT_CTOR(NsAmMitmService) { /* ... */ } diff --git a/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.hpp b/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.hpp index 9fcf23008..3b2c7b9f2 100644 --- a/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.hpp @@ -59,7 +59,7 @@ namespace ams::mitm::ns { /* We will mitm: * - web applets, to facilitate hbl web browser launching. */ - return ncm::IsWebAppletProgramId(client_info.program_id); + return ncm::IsWebAppletId(client_info.program_id); } public: SF_MITM_SERVICE_OBJECT_CTOR(NsWebMitmService) { /* ... */ } diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp index d9736ff7d..25becb56f 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp @@ -22,12 +22,12 @@ namespace ams::mitm::settings { Result SetMitmService::EnsureLocale() { std::scoped_lock lk(this->lock); - const bool is_ns = this->client_info.program_id == ncm::ProgramId::Ns; + const bool is_ns = this->client_info.program_id == ncm::SystemProgramId::Ns; if (!this->got_locale) { std::memset(&this->locale, 0xCC, sizeof(this->locale)); ncm::ProgramId program_id = this->client_info.program_id; - if (program_id == ncm::ProgramId::Ns) { + if (is_ns) { /* When NS asks for a locale, refresh to get the current application locale. */ os::ProcessId application_process_id; R_TRY(pm::dmnt::GetApplicationProcessId(&application_process_id)); diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp index 605569646..778f3748d 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp @@ -33,8 +33,8 @@ namespace ams::mitm::settings { /* We will mitm: * - ns and games, to allow for overriding game locales. */ - const bool is_game = (ncm::IsApplicationProgramId(client_info.program_id) && !client_info.override_status.IsHbl()); - return client_info.program_id == ncm::ProgramId::Ns || is_game; + const bool is_game = (ncm::IsApplicationId(client_info.program_id) && !client_info.override_status.IsHbl()); + return client_info.program_id == ncm::SystemProgramId::Ns || is_game; } public: SF_MITM_SERVICE_OBJECT_CTOR(SetMitmService) { diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp index fc3425dce..dc2cf1b35 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp @@ -35,17 +35,17 @@ namespace ams::mitm::settings { } /* Mount firmware version data archive. */ - R_ABORT_UNLESS(romfsMountFromDataArchive(static_cast(ncm::ProgramId::ArchiveSystemVersion), NcmStorageId_BuiltInSystem, "sysver")); { - ON_SCOPE_EXIT { romfsUnmount("sysver"); }; + R_ABORT_UNLESS(ams::fs::MountSystemData("sysver", ncm::SystemDataId::SystemVersion)); + ON_SCOPE_EXIT { ams::fs::Unmount("sysver"); }; /* Firmware version file must exist. */ - FILE *fp = fopen("sysver:/file", "rb"); - AMS_ABORT_UNLESS(fp != nullptr); - ON_SCOPE_EXIT { fclose(fp); }; + ams::fs::FileHandle file; + R_ABORT_UNLESS(ams::fs::OpenFile(std::addressof(file), "sysver:/file", fs::OpenMode_Read)); + ON_SCOPE_EXIT { ams::fs::CloseFile(file); }; /* Must be possible to read firmware version from file. */ - AMS_ABORT_UNLESS(fread(&g_firmware_version, sizeof(g_firmware_version), 1, fp) == 1); + R_ABORT_UNLESS(ams::fs::ReadFile(file, 0, std::addressof(g_firmware_version), sizeof(g_firmware_version))); g_ams_firmware_version = g_firmware_version; } @@ -75,7 +75,7 @@ namespace ams::mitm::settings { /* We want to give a special firmware version to the home menu title, and nothing else. */ /* This means Qlaunch + Maintenance Menu, and nothing else. */ - if (client_info.program_id == ncm::ProgramId::AppletQlaunch || client_info.program_id == ncm::ProgramId::AppletMaintenanceMenu) { + if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) { *out = g_ams_firmware_version; } else { *out = g_firmware_version; diff --git a/stratosphere/boot/source/boot_i2c_utils.cpp b/stratosphere/boot/source/boot_i2c_utils.cpp index 47185d438..8d5a8f9a5 100644 --- a/stratosphere/boot/source/boot_i2c_utils.cpp +++ b/stratosphere/boot/source/boot_i2c_utils.cpp @@ -27,7 +27,7 @@ namespace ams::boot { u64 cur_time = 0; while (true) { const auto retry_result = f(); - R_UNLESS(R_FAILED(retry_result), ResultSuccess()); + R_SUCCEED_IF(R_SUCCEEDED(retry_result)); cur_time += retry_interval; if (cur_time < timeout) { diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index 6494f8857..857da11b1 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -52,7 +52,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Boot; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Boot; void ExceptionHandler(FatalErrorContext *ctx) { /* We're boot sysmodule, so manually reboot to fatal error. */ @@ -100,7 +100,6 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ - fsdevUnmountAll(); pmshellExit(); splExit(); fsExit(); diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp index 16c53a487..78bd68e31 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp @@ -379,7 +379,8 @@ namespace ams::i2c::driver::impl { /* Wait for flush to finish, check every ms for 5 ms. */ for (size_t i = 0; i < 5; i++) { - R_UNLESS((reg::Read(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3), ResultSuccess()); + const bool flush_done = (reg::Read(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3) == 0; + R_SUCCEED_IF(flush_done); svcSleepThread(1'000'000ul); } @@ -418,7 +419,8 @@ namespace ams::i2c::driver::impl { Result BusAccessor::GetAndHandleTransactionResult() { const auto transaction_result = this->GetTransactionResult(); - R_UNLESS(R_FAILED(transaction_result), ResultSuccess()); + R_SUCCEED_IF(R_SUCCEEDED(transaction_result)); + this->HandleTransactionResult(transaction_result); this->ClearInterruptMask(); this->interrupt_event.Reset(); diff --git a/stratosphere/boot2/source/boot2_main.cpp b/stratosphere/boot2/source/boot2_main.cpp index 00f62adc8..37c2f6aa9 100644 --- a/stratosphere/boot2/source/boot2_main.cpp +++ b/stratosphere/boot2/source/boot2_main.cpp @@ -38,7 +38,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Boot2; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Boot2; namespace result { @@ -79,13 +79,14 @@ void __appInit(void) { R_ABORT_UNLESS(gpioInitialize()); }); - R_ABORT_UNLESS(fsdevMountSdmc()); + /* Mount the SD card. */ + R_ABORT_UNLESS(fs::MountSdCard("sdmc")); ams::CheckApiVersion(); } void __appExit(void) { - fsdevUnmountAll(); + fs::Unmount("sdmc"); gpioExit(); setsysExit(); pmshellExit(); @@ -96,6 +97,7 @@ void __appExit(void) { int main(int argc, char **argv) { + /* Launch all programs off of SYSTEM/the SD. */ boot2::LaunchPostSdCardBootPrograms(); } diff --git a/stratosphere/creport/source/creport_main.cpp b/stratosphere/creport/source/creport_main.cpp index 025210fc6..61d935512 100644 --- a/stratosphere/creport/source/creport_main.cpp +++ b/stratosphere/creport/source/creport_main.cpp @@ -40,7 +40,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Creport; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Creport; namespace result { diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 3b3ede1ab..b94d9c83d 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -602,7 +602,7 @@ namespace ams::dmnt::cheat::impl { { if (this->HasActiveCheatProcess()) { /* When forcing attach, we're done. */ - R_UNLESS(on_process_launch, ResultSuccess()); + R_SUCCEED_IF(!on_process_launch); } /* Detach from the current process, if it's open. */ diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index 25ab804f6..119cb65bd 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -35,7 +35,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Dmnt; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Dmnt; namespace result { @@ -71,7 +71,7 @@ void __appInit(void) { R_ABORT_UNLESS(roDmntInitialize()); } R_ABORT_UNLESS(nsdevInitialize()); - R_ABORT_UNLESS(lrInitialize()); + lr::Initialize(); R_ABORT_UNLESS(setInitialize()); R_ABORT_UNLESS(setsysInitialize()); R_ABORT_UNLESS(hidInitialize()); @@ -90,7 +90,7 @@ void __appExit(void) { hidExit(); setsysExit(); setExit(); - lrExit(); + lr::Finalize(); nsdevExit(); roDmntExit(); ldrDmntExit(); diff --git a/stratosphere/dmnt/source/dmnt_service_target_io.cpp b/stratosphere/dmnt/source/dmnt_service_target_io.cpp index 50ffbd995..41ac24abf 100644 --- a/stratosphere/dmnt/source/dmnt_service_target_io.cpp +++ b/stratosphere/dmnt/source/dmnt_service_target_io.cpp @@ -49,7 +49,7 @@ namespace ams::dmnt { Result EnsureSdInitialized() { std::scoped_lock lk(g_sd_lock); - R_UNLESS(!g_sd_initialized, ResultSuccess()); + R_SUCCEED_IF(g_sd_initialized); R_TRY(fsOpenSdCardFileSystem(&g_sd_fs)); g_sd_initialized = true; diff --git a/stratosphere/eclct.stub/source/eclct_stub.cpp b/stratosphere/eclct.stub/source/eclct_stub.cpp index 10f651726..fe9e52254 100644 --- a/stratosphere/eclct.stub/source/eclct_stub.cpp +++ b/stratosphere/eclct.stub/source/eclct_stub.cpp @@ -36,7 +36,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Eclct; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Eclct; namespace result { diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 50afb99a1..1f6735842 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -44,7 +44,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Fatal; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Fatal; namespace result { diff --git a/stratosphere/fatal/source/fatal_service.cpp b/stratosphere/fatal/source/fatal_service.cpp index 3f895cac6..f5c5fa198 100644 --- a/stratosphere/fatal/source/fatal_service.cpp +++ b/stratosphere/fatal/source/fatal_service.cpp @@ -64,8 +64,8 @@ namespace ams::fatal::srv { /* Throw implementation. */ Result ServiceContext::ThrowFatalWithCpuContext(Result result, os::ProcessId process_id, FatalPolicy policy, const CpuContext &cpu_ctx) { - /* We don't support Error Report only fatals. */ - R_UNLESS(policy != FatalPolicy_ErrorReport, ResultSuccess()); + /* We don't support Error-Report-only fatals. */ + R_SUCCEED_IF(policy == FatalPolicy_ErrorReport); /* Note that we've thrown fatal. */ R_TRY(this->TrySetHasThrown()); @@ -83,7 +83,7 @@ namespace ams::fatal::srv { /* Get program id. */ pm::info::GetProgramId(&this->context.program_id, process_id); - this->context.is_creport = (this->context.program_id == ncm::ProgramId::Creport); + this->context.is_creport = (this->context.program_id == ncm::SystemProgramId::Creport); if (!this->context.is_creport) { /* On firmware version 2.0.0, use debugging SVCs to collect information. */ diff --git a/stratosphere/loader/source/ldr_anti_downgrade_tables.inc b/stratosphere/loader/source/ldr_anti_downgrade_tables.inc index 5184d8a65..30f6498f0 100644 --- a/stratosphere/loader/source/ldr_anti_downgrade_tables.inc +++ b/stratosphere/loader/source/ldr_anti_downgrade_tables.inc @@ -23,158 +23,158 @@ constexpr u32 MakeSystemVersion(u32 major, u32 minor, u32 micro) { } constexpr MinimumProgramVersion g_MinimumProgramVersions810[] = { - {ncm::ProgramId::Settings, 1}, - {ncm::ProgramId::Bus, 1}, - {ncm::ProgramId::Audio, 1}, - {ncm::ProgramId::NvServices, 1}, - {ncm::ProgramId::Ns, 1}, - {ncm::ProgramId::Ssl, 1}, - {ncm::ProgramId::Es, 1}, - {ncm::ProgramId::Creport, 1}, - {ncm::ProgramId::Ro, 1}, + {ncm::SystemProgramId::Settings, 1}, + {ncm::SystemProgramId::Bus, 1}, + {ncm::SystemProgramId::Audio, 1}, + {ncm::SystemProgramId::NvServices, 1}, + {ncm::SystemProgramId::Ns, 1}, + {ncm::SystemProgramId::Ssl, 1}, + {ncm::SystemProgramId::Es, 1}, + {ncm::SystemProgramId::Creport, 1}, + {ncm::SystemProgramId::Ro, 1}, }; constexpr size_t g_MinimumProgramVersionsCount810 = util::size(g_MinimumProgramVersions810); constexpr MinimumProgramVersion g_MinimumProgramVersions900[] = { /* All non-Development System Modules. */ - {ncm::ProgramId::Usb, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Tma, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Boot2, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Settings, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Bus, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Bluetooth, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Bcat, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Dmnt, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Friends, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Nifm, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Ptm, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Shell, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::BsdSockets, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Hid, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Audio, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::LogManager, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Wlan, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Cs, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Ldn, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::NvServices, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Pcv, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Ppc, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::NvnFlinger, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Pcie, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Account, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Ns, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Nfc, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Psc, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::CapSrv, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Am, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Ssl, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Nim, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Cec, MakeSystemVersion(9, 0, 0)}, */ - /* {ncm::ProgramId::Tspm, MakeSystemVersion(9, 0, 0)}, */ - /* {ncm::ProgramId::Spl, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Lbl, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Btm, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Erpt, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Time, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Vi, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Pctl, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Npns, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Eupld, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Glue, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Eclct, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Es, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Fatal, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Grc, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Creport, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Ro, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Profiler, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Sdb, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Migration, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Jit, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::JpegDec, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::SafeMode, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Olsc, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Dt, MakeSystemVersion(9, 0, 0)}, */ - /* {ncm::ProgramId::Nd, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Ngct, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Usb, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Tma, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Boot2, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Settings, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Bus, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Bluetooth, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Bcat, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Dmnt, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Friends, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Nifm, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Ptm, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Shell, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::BsdSockets, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Hid, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Audio, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::LogManager, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Wlan, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Cs, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Ldn, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::NvServices, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Pcv, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Ppc, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::NvnFlinger, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Pcie, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Account, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Ns, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Nfc, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Psc, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::CapSrv, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Am, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Ssl, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Nim, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Cec, MakeSystemVersion(9, 0, 0)}, */ + /* {ncm::SystemProgramId::Tspm, MakeSystemVersion(9, 0, 0)}, */ + /* {ncm::SystemProgramId::Spl, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Lbl, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Btm, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Erpt, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Time, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Vi, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Pctl, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Npns, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Eupld, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Glue, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Eclct, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Es, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Fatal, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Grc, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Creport, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Ro, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Profiler, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Sdb, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Migration, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Jit, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::JpegDec, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::SafeMode, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Olsc, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Dt, MakeSystemVersion(9, 0, 0)}, */ + /* {ncm::SystemProgramId::Nd, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Ngct, MakeSystemVersion(9, 0, 0)}, /* All Web Applets. */ - {ncm::ProgramId::AppletWeb, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::AppletShop, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::AppletOfflineWeb, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::AppletLoginShare, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::AppletWifiWebAuth, MakeSystemVersion(9, 0, 0)}, + {ncm::WebAppletId::Web, MakeSystemVersion(9, 0, 0)}, + {ncm::WebAppletId::Shop, MakeSystemVersion(9, 0, 0)}, + {ncm::WebAppletId::OfflineWeb, MakeSystemVersion(9, 0, 0)}, + {ncm::WebAppletId::LoginShare, MakeSystemVersion(9, 0, 0)}, + {ncm::WebAppletId::WifiWebAuth, MakeSystemVersion(9, 0, 0)}, }; constexpr size_t g_MinimumProgramVersionsCount900 = util::size(g_MinimumProgramVersions900); constexpr MinimumProgramVersion g_MinimumProgramVersions910[] = { /* All non-Development System Modules. */ - {ncm::ProgramId::Usb, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Tma, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Boot2, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Settings, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Bus, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Bluetooth, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Bcat, MakeSystemVersion(9, 1, 0)}, - /* {ncm::ProgramId::Dmnt, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Friends, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Nifm, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Ptm, MakeSystemVersion(9, 0, 0)}, - /* {ncm::ProgramId::Shell, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::BsdSockets, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Hid, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Audio, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::LogManager, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Wlan, MakeSystemVersion(9, 1, 0)}, - /* {ncm::ProgramId::Cs, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Ldn, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::NvServices, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Pcv, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Ppc, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::NvnFlinger, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Pcie, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Account, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Ns, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Nfc, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Psc, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::CapSrv, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Am, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Ssl, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Nim, MakeSystemVersion(9, 1, 0)}, - /* {ncm::ProgramId::Cec, MakeSystemVersion(9, 0, 0)}, */ - /* {ncm::ProgramId::Tspm, MakeSystemVersion(9, 0, 0)}, */ - /* {ncm::ProgramId::Spl, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Lbl, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Btm, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Erpt, MakeSystemVersion(9, 1, 0)}, - /* {ncm::ProgramId::Time, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Vi, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Pctl, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Npns, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Eupld, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Glue, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Eclct, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Es, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Fatal, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Grc, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Creport, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::Ro, MakeSystemVersion(9, 1, 0)}, - /* {ncm::ProgramId::Profiler, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Sdb, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Migration, MakeSystemVersion(9, 1, 0)}, - /* {ncm::ProgramId::Jit, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::JpegDec, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::SafeMode, MakeSystemVersion(9, 0, 0)}, - {ncm::ProgramId::Olsc, MakeSystemVersion(9, 1, 0)}, - /* {ncm::ProgramId::Dt, MakeSystemVersion(9, 0, 0)}, */ - /* {ncm::ProgramId::Nd, MakeSystemVersion(9, 0, 0)}, */ - {ncm::ProgramId::Ngct, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Usb, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Tma, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Boot2, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Settings, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Bus, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Bluetooth, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Bcat, MakeSystemVersion(9, 1, 0)}, + /* {ncm::SystemProgramId::Dmnt, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Friends, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Nifm, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Ptm, MakeSystemVersion(9, 0, 0)}, + /* {ncm::SystemProgramId::Shell, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::BsdSockets, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Hid, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Audio, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::LogManager, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Wlan, MakeSystemVersion(9, 1, 0)}, + /* {ncm::SystemProgramId::Cs, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Ldn, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::NvServices, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Pcv, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Ppc, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::NvnFlinger, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Pcie, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Account, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Ns, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Nfc, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Psc, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::CapSrv, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Am, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Ssl, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Nim, MakeSystemVersion(9, 1, 0)}, + /* {ncm::SystemProgramId::Cec, MakeSystemVersion(9, 0, 0)}, */ + /* {ncm::SystemProgramId::Tspm, MakeSystemVersion(9, 0, 0)}, */ + /* {ncm::SystemProgramId::Spl, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Lbl, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Btm, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Erpt, MakeSystemVersion(9, 1, 0)}, + /* {ncm::SystemProgramId::Time, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Vi, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Pctl, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Npns, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Eupld, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Glue, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Eclct, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Es, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Fatal, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Grc, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Creport, MakeSystemVersion(9, 1, 0)}, + {ncm::SystemProgramId::Ro, MakeSystemVersion(9, 1, 0)}, + /* {ncm::SystemProgramId::Profiler, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Sdb, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Migration, MakeSystemVersion(9, 1, 0)}, + /* {ncm::SystemProgramId::Jit, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::JpegDec, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::SafeMode, MakeSystemVersion(9, 0, 0)}, + {ncm::SystemProgramId::Olsc, MakeSystemVersion(9, 1, 0)}, + /* {ncm::SystemProgramId::Dt, MakeSystemVersion(9, 0, 0)}, */ + /* {ncm::SystemProgramId::Nd, MakeSystemVersion(9, 0, 0)}, */ + {ncm::SystemProgramId::Ngct, MakeSystemVersion(9, 1, 0)}, /* All Web Applets. */ - {ncm::ProgramId::AppletWeb, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::AppletShop, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::AppletOfflineWeb, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::AppletLoginShare, MakeSystemVersion(9, 1, 0)}, - {ncm::ProgramId::AppletWifiWebAuth, MakeSystemVersion(9, 1, 0)}, + {ncm::WebAppletId::Web, MakeSystemVersion(9, 1, 0)}, + {ncm::WebAppletId::Shop, MakeSystemVersion(9, 1, 0)}, + {ncm::WebAppletId::OfflineWeb, MakeSystemVersion(9, 1, 0)}, + {ncm::WebAppletId::LoginShare, MakeSystemVersion(9, 1, 0)}, + {ncm::WebAppletId::WifiWebAuth, MakeSystemVersion(9, 1, 0)}, }; constexpr size_t g_MinimumProgramVersionsCount910 = util::size(g_MinimumProgramVersions910); diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index 31cccea05..a837941e3 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -264,52 +264,54 @@ namespace ams::ldr { /* Redirection API. */ Result ResolveContentPath(char *out_path, const ncm::ProgramLocation &loc) { - char path[FS_MAX_PATH]; + lr::Path path; /* Try to get the path from the registered resolver. */ - LrRegisteredLocationResolver reg; - R_TRY(lrOpenRegisteredLocationResolver(®)); - ON_SCOPE_EXIT { serviceClose(®.s); }; + lr::RegisteredLocationResolver reg; + R_TRY(lr::OpenRegisteredLocationResolver(std::addressof(reg))); - R_TRY_CATCH(lrRegLrResolveProgramPath(®, static_cast(loc.program_id), path)) { + R_TRY_CATCH(reg.ResolveProgramPath(std::addressof(path), loc.program_id)) { R_CATCH(lr::ResultProgramNotFound) { /* Program wasn't found via registered resolver, fall back to the normal resolver. */ - LrLocationResolver lr; - R_TRY(lrOpenLocationResolver(static_cast(loc.storage_id), &lr)); - ON_SCOPE_EXIT { serviceClose(&lr.s); }; - - R_TRY(lrLrResolveProgramPath(&lr, static_cast(loc.program_id), path)); + lr::LocationResolver lr; + R_TRY(lr::OpenLocationResolver(std::addressof(lr), static_cast(loc.storage_id))); + R_TRY(lr.ResolveProgramPath(std::addressof(path), loc.program_id)); } } R_END_TRY_CATCH; - std::strncpy(out_path, path, FS_MAX_PATH); - out_path[FS_MAX_PATH - 1] = '\0'; + std::strncpy(out_path, path.str, fs::EntryNameLengthMax); + out_path[fs::EntryNameLengthMax - 1] = '\0'; FixFileSystemPath(out_path); return ResultSuccess(); } Result RedirectContentPath(const char *path, const ncm::ProgramLocation &loc) { - LrLocationResolver lr; - R_TRY(lrOpenLocationResolver(static_cast(loc.storage_id), &lr)); - ON_SCOPE_EXIT { serviceClose(&lr.s); }; + /* Copy in path. */ + lr::Path lr_path; + std::strncpy(lr_path.str, path, sizeof(lr_path.str)); + lr_path.str[sizeof(lr_path.str) - 1] = '\0'; - return lrLrRedirectProgramPath(&lr, static_cast(loc.program_id), path); + /* Redirect the path. */ + lr::LocationResolver lr; + R_TRY(lr::OpenLocationResolver(std::addressof(lr), static_cast(loc.storage_id))); + lr.RedirectProgramPath(lr_path, loc.program_id); + + return ResultSuccess(); } Result RedirectHtmlDocumentPathForHbl(const ncm::ProgramLocation &loc) { - char path[FS_MAX_PATH]; + lr::Path path; /* Open a location resolver. */ - LrLocationResolver lr; - R_TRY(lrOpenLocationResolver(static_cast(loc.storage_id), &lr)); - ON_SCOPE_EXIT { serviceClose(&lr.s); }; + lr::LocationResolver lr; + R_TRY(lr::OpenLocationResolver(std::addressof(lr), static_cast(loc.storage_id))); /* If there's already a Html Document path, we don't need to set one. */ - R_UNLESS(R_FAILED(lrLrResolveApplicationHtmlDocumentPath(&lr, static_cast(loc.program_id), path)), ResultSuccess()); + R_SUCCEED_IF(R_SUCCEEDED(lr.ResolveApplicationHtmlDocumentPath(std::addressof(path), loc.program_id))); /* We just need to set this to any valid NCA path. Let's use the executable path. */ - R_TRY(lrLrResolveProgramPath(&lr, static_cast(loc.program_id), path)); - R_TRY(lrLrRedirectApplicationHtmlDocumentPath(&lr, static_cast(loc.program_id), static_cast(loc.program_id), path)); + R_TRY(lr.ResolveProgramPath(std::addressof(path), loc.program_id)); + lr.RedirectApplicationHtmlDocumentPath(path, loc.program_id, loc.program_id); return ResultSuccess(); } diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index b9c5b5162..327e91e92 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -39,7 +39,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Loader; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Loader; namespace result { @@ -73,7 +73,7 @@ void __appInit(void) { /* Initialize services we need. */ sm::DoWithSession([&]() { R_ABORT_UNLESS(fsInitialize()); - R_ABORT_UNLESS(lrInitialize()); + lr::Initialize(); R_ABORT_UNLESS(fsldrInitialize()); }); @@ -84,7 +84,7 @@ void __appExit(void) { /* Cleanup services. */ fsdevUnmountAll(); fsldrExit(); - lrExit(); + lr::Finalize(); fsExit(); } diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index a6e156e1f..2ef0b4146 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -84,7 +84,10 @@ namespace ams::ldr { #include "ldr_anti_downgrade_tables.inc" Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) { - R_UNLESS(hos::GetVersion() >= hos::Version_810, ResultSuccess()); + /* No version verification is done before 8.1.0. */ + R_SUCCEED_IF(hos::GetVersion() < hos::Version_810); + + /* Do version-dependent validation, if compiled to do so. */ #ifdef LDR_VALIDATE_PROCESS_VERSION const MinimumProgramVersion *entries = nullptr; size_t num_entries = 0; diff --git a/stratosphere/ncm/Makefile b/stratosphere/ncm/Makefile new file mode 100644 index 000000000..a92187ef8 --- /dev/null +++ b/stratosphere/ncm/Makefile @@ -0,0 +1,122 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + + +CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ + $(notdir $(wildcard $(dir)/*.c)))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) + +CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ + $(notdir $(wildcard $(dir)/*.cpp)))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) + +SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ + $(notdir $(wildcard $(dir)/*.s)))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).kip $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).kip + +$(OUTPUT).kip : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/ncm/ncm.json b/stratosphere/ncm/ncm.json new file mode 100644 index 000000000..5a3ac657b --- /dev/null +++ b/stratosphere/ncm/ncm.json @@ -0,0 +1,67 @@ +{ + "name": "NCM", + "title_id": "0x0100000000000002", + "main_thread_stack_size": "0x00004000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 1, + "kernel_capabilities": [ + { + "type": "handle_table_size", + "value": 128 + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize" : "0x01", + "svcSetMemoryPermission" : "0x02", + "svcSetMemoryAttribute" : "0x03", + "svcMapMemory" : "0x04", + "svcUnmapMemory" : "0x05", + "svcQueryMemory" : "0x06", + "svcExitProcess" : "0x07", + "svcCreateThread" : "0x08", + "svcStartThread" : "0x09", + "svcExitThread" : "0x0A", + "svcSleepThread" : "0x0B", + "svcGetThreadPriority" : "0x0C", + "svcSetThreadPriority" : "0x0D", + "svcGetThreadCoreMask" : "0x0E", + "svcSetThreadCoreMask" : "0x0F", + "svcGetCurrentProcessorNumber" : "0x10", + "svcSignalEvent" : "0x11", + "svcClearEvent" : "0x12", + "svcMapSharedMemory" : "0x13", + "svcUnmapSharedMemory" : "0x14", + "svcCreateTransferMemory" : "0x15", + "svcCloseHandle" : "0x16", + "svcResetSignal" : "0x17", + "svcWaitSynchronization" : "0x18", + "svcCancelSynchronization" : "0x19", + "svcArbitrateLock" : "0x1A", + "svcArbitrateUnlock" : "0x1B", + "svcWaitProcessWideKeyAtomic" : "0x1C", + "svcSignalProcessWideKey" : "0x1D", + "svcGetSystemTick" : "0x1E", + "svcConnectToNamedPort" : "0x1F", + "svcSendSyncRequestLight" : "0x20", + "svcSendSyncRequest" : "0x21", + "svcSendSyncRequestWithUserBuffer" : "0x22", + "svcSendAsyncRequestWithUserBuffer" : "0x23", + "svcGetProcessId" : "0x24", + "svcGetThreadId" : "0x25", + "svcBreak" : "0x26", + "svcOutputDebugString" : "0x27", + "svcReturnFromException" : "0x28", + "svcGetInfo" : "0x29", + "svcWaitForAddress" : "0x34", + "svcSignalToAddress" : "0x35", + "svcCreateSession" : "0x40", + "svcAcceptSession" : "0x41", + "svcReplyAndReceiveLight" : "0x42", + "svcReplyAndReceive" : "0x43", + "svcReplyAndReceiveWithUserBuffer" : "0x44", + "svcCallSecureMonitor" : "0x7F" + } + } + ] +} \ No newline at end of file diff --git a/stratosphere/ncm/source/ncm_main.cpp b/stratosphere/ncm/source/ncm_main.cpp new file mode 100644 index 000000000..1963c82d7 --- /dev/null +++ b/stratosphere/ncm/source/ncm_main.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x400000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); +} + +namespace ams { + + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Ncm; + + namespace result { + + bool CallFatalOnResultAssertion = false; + + } + +} + +using namespace ams; + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + ams::CrashHandler(ctx); +} + +void __libnx_initheap(void) { + void * addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char *fake_heap_start; + extern char *fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; +} + +void __appInit(void) { + hos::SetVersionForLibnx(); + + sm::DoWithSession([&]() { + R_ABORT_UNLESS(fsInitialize()); + R_ABORT_UNLESS(splInitialize()); + }); + + ams::CheckApiVersion(); +} + +void __appExit(void) { + /* Cleanup services. */ + splExit(); + fsExit(); +} + +namespace { + + struct ContentManagerServerOptions { + static constexpr size_t PointerBufferSize = 0x400; + static constexpr size_t MaxDomains = 0; + static constexpr size_t MaxDomainObjects = 0; + }; + + constexpr inline size_t ContentManagerNumServers = 1; + constexpr inline size_t ContentManagerManagerSessions = 16; + constexpr inline size_t ContentManagerExtraSessions = 16; + constexpr inline size_t ContentManagerMaxSessions = ContentManagerManagerSessions + ContentManagerExtraSessions; + + constexpr inline sm::ServiceName ContentManagerServiceName = sm::ServiceName::Encode("ncm"); + + class ContentManagerServerManager : public sf::hipc::ServerManager { + private: + static constexpr size_t ThreadStackSize = 0x4000; + static constexpr int ThreadPriority = 0x15; + + using ServiceType = ncm::ContentManagerImpl; + private: + os::StaticThread thread; + std::shared_ptr ncm_manager; + private: + static void ThreadFunction(void *_this) { + reinterpret_cast(_this)->LoopProcess(); + } + public: + ContentManagerServerManager(ServiceType *m) + : thread(ThreadFunction, this, ThreadPriority), ncm_manager() + { + /* ... */ + } + + ams::Result Initialize(std::shared_ptr manager_obj) { + this->ncm_manager = manager_obj; + return this->RegisterServer(ContentManagerServiceName, ContentManagerManagerSessions, this->ncm_manager); + } + + ams::Result StartThreads() { + return this->thread.Start(); + } + + void Wait() { + this->thread.Join(); + } + }; + + struct LocationResolverServerOptions { + static constexpr size_t PointerBufferSize = 0x400; + static constexpr size_t MaxDomains = 0; + static constexpr size_t MaxDomainObjects = 0; + }; + + constexpr inline size_t LocationResolverNumServers = 1; + constexpr inline size_t LocationResolverManagerSessions = 16; + constexpr inline size_t LocationResolverExtraSessions = 16; + constexpr inline size_t LocationResolverMaxSessions = LocationResolverManagerSessions + LocationResolverExtraSessions; + + constexpr inline sm::ServiceName LocationResolverServiceName = sm::ServiceName::Encode("lr"); + + class LocationResolverServerManager : public sf::hipc::ServerManager { + private: + static constexpr size_t ThreadStackSize = 0x4000; + static constexpr int ThreadPriority = 0x15; + + using ServiceType = lr::LocationResolverManagerImpl; + private: + os::StaticThread thread; + std::shared_ptr lr_manager; + private: + static void ThreadFunction(void *_this) { + reinterpret_cast(_this)->LoopProcess(); + } + public: + LocationResolverServerManager(ServiceType *m) + : thread(ThreadFunction, this, ThreadPriority), lr_manager(sf::ServiceObjectTraits::SharedPointerHelper::GetEmptyDeleteSharedPointer(m)) + { + /* ... */ + } + + ams::Result Initialize() { + return this->RegisterServer(LocationResolverServiceName, LocationResolverManagerSessions, this->lr_manager); + } + + ams::Result StartThreads() { + return this->thread.Start(); + } + + void Wait() { + this->thread.Join(); + } + }; + + ncm::ContentManagerImpl g_ncm_manager_service_object; + ContentManagerServerManager g_ncm_server_manager(std::addressof(g_ncm_manager_service_object)); + + lr::LocationResolverManagerImpl g_lr_manager_service_object; + LocationResolverServerManager g_lr_server_manager(std::addressof(g_lr_manager_service_object)); + + ALWAYS_INLINE std::shared_ptr GetSharedPointerToContentManager() { + return sf::ServiceObjectTraits::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(g_ncm_manager_service_object)); + } + + /* Compile-time configuration. */ +#ifdef NCM_BUILD_FOR_INTITIALIZE + constexpr inline bool BuildSystemDatabase = true; +#else + constexpr inline bool BuildSystemDatabase = false; +#endif + +#ifdef NCM_BUILD_FOR_SAFEMODE + constexpr inline bool ImportSystemDatabaseFromSignedSystemPartitionOnSdCard = true; +#else + constexpr inline bool ImportSystemDatabaseFromSignedSystemPartitionOnSdCard = false; +#endif + + static_assert(!(BuildSystemDatabase && ImportSystemDatabaseFromSignedSystemPartitionOnSdCard), "Invalid NCM build configuration!"); + + constexpr inline ncm::ContentManagerConfig ManagerConfig = { BuildSystemDatabase, ImportSystemDatabaseFromSignedSystemPartitionOnSdCard }; + +} + +int main(int argc, char **argv) +{ + /* Create and initialize the content manager. */ + auto content_manager = GetSharedPointerToContentManager(); + R_ABORT_UNLESS(content_manager->Initialize(ManagerConfig)); + + /* Initialize ncm's server and start threads. */ + R_ABORT_UNLESS(g_ncm_server_manager.Initialize(content_manager)); + R_ABORT_UNLESS(g_ncm_server_manager.StartThreads()); + + /* Initialize ncm api. */ + ncm::InitializeWithObject(content_manager); + + /* Initialize lr's server and start threads. */ + R_ABORT_UNLESS(g_lr_server_manager.Initialize()); + R_ABORT_UNLESS(g_lr_server_manager.StartThreads()); + + /* Wait indefinitely. */ + g_ncm_server_manager.Wait(); + g_lr_server_manager.Wait(); + + return 0; +} diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 1f3feb7b1..de092f277 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -352,7 +352,7 @@ namespace ams::pm::impl { /* Process hooks/signaling. */ if (location.program_id == g_program_id_hook) { g_hook_to_create_process_event.Signal(); - g_program_id_hook = ncm::ProgramId::Invalid; + g_program_id_hook = ncm::InvalidProgramId; } else if (is_application && g_application_hook) { g_hook_to_create_application_process_event.Signal(); g_application_hook = false; @@ -666,7 +666,7 @@ namespace ams::pm::impl { *out_hook = INVALID_HANDLE; { - ncm::ProgramId old_value = ncm::ProgramId::Invalid; + ncm::ProgramId old_value = ncm::InvalidProgramId; R_UNLESS(g_program_id_hook.compare_exchange_strong(old_value, program_id), pm::ResultDebugHookInUse()); } @@ -688,7 +688,7 @@ namespace ams::pm::impl { Result ClearHook(u32 which) { if (which & HookType_ProgramId) { - g_program_id_hook = ncm::ProgramId::Invalid; + g_program_id_hook = ncm::InvalidProgramId; } if (which & HookType_Application) { g_application_hook = false; diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 46e8fd594..b963dd883 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -41,7 +41,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Pm; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Pm; namespace result { @@ -141,7 +141,6 @@ void __appInit(void) { /* Use AMS manager extension to tell SM that FS has been worked around. */ R_ABORT_UNLESS(sm::manager::EndInitialDefers()); - R_ABORT_UNLESS(lrInitialize()); R_ABORT_UNLESS(ldrPmInitialize()); R_ABORT_UNLESS(splInitialize()); }); @@ -153,7 +152,6 @@ void __appExit(void) { /* Cleanup services. */ splExit(); ldrPmExit(); - lrExit(); smManagerExit(); fsprExit(); } diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index 88ddee35a..93a87227a 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -87,7 +87,7 @@ namespace ams::ro::impl { process_h = other_process_h; } - ncm::ProgramId program_id = ncm::ProgramId::Invalid; + ncm::ProgramId program_id = ncm::InvalidProgramId; if (hos::GetVersion() >= hos::Version_300) { /* 3.0.0+: Use svcGetInfo. */ R_ABORT_UNLESS(svcGetInfo(&program_id.value, InfoType_ProgramId, process_h, 0)); diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index b27ddb87a..36344558f 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -34,7 +34,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Ro; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Ro; namespace result { diff --git a/stratosphere/sm/source/impl/sm_service_manager.cpp b/stratosphere/sm/source/impl/sm_service_manager.cpp index e5b5e49b9..19248a7e3 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.cpp +++ b/stratosphere/sm/source/impl/sm_service_manager.cpp @@ -239,10 +239,13 @@ namespace ams::sm::impl { bool IsMitmDisallowed(ncm::ProgramId program_id) { /* Mitm used on certain programs can prevent the boot process from completing. */ /* TODO: Is there a way to do this that's less hardcoded? Needs design thought. */ - return program_id == ncm::ProgramId::Loader || - program_id == ncm::ProgramId::Boot || - program_id == ncm::ProgramId::AtmosphereMitm || - program_id == ncm::ProgramId::Creport; + return program_id == ncm::SystemProgramId::Loader || + program_id == ncm::SystemProgramId::Pm || + program_id == ncm::SystemProgramId::Spl || + program_id == ncm::SystemProgramId::Boot || + program_id == ncm::SystemProgramId::Ncm || + program_id == ncm::AtmosphereProgramId::Mitm || + program_id == ncm::SystemProgramId::Creport; } Result AddFutureMitmDeclaration(ServiceName service) { @@ -297,7 +300,7 @@ namespace ams::sm::impl { is_valid &= std::memcmp(&ac_service, &service, access_control.GetServiceNameSize() - 1) == 0; } - R_UNLESS(!is_valid, ResultSuccess()); + R_SUCCEED_IF(is_valid); } access_control = access_control.GetNextEntry(); } diff --git a/stratosphere/sm/source/sm_main.cpp b/stratosphere/sm/source/sm_main.cpp index 3fc050a3e..b588397ca 100644 --- a/stratosphere/sm/source/sm_main.cpp +++ b/stratosphere/sm/source/sm_main.cpp @@ -40,7 +40,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Sm; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Sm; namespace result { diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index 7e6af932f..230bf9e8b 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -19,7 +19,7 @@ namespace ams::sm { Result ManagerService::RegisterProcess(os::ProcessId process_id, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac) { - return impl::RegisterProcess(process_id, ncm::ProgramId::Invalid, cfg::OverrideStatus{}, acid_sac.GetPointer(), acid_sac.GetSize(), aci_sac.GetPointer(), aci_sac.GetSize()); + return impl::RegisterProcess(process_id, ncm::InvalidProgramId, cfg::OverrideStatus{}, acid_sac.GetPointer(), acid_sac.GetSize(), aci_sac.GetPointer(), aci_sac.GetSize()); } Result ManagerService::UnregisterProcess(os::ProcessId process_id) { diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index f08a6a165..32fb16f05 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -46,7 +46,7 @@ extern "C" { namespace ams { - ncm::ProgramId CurrentProgramId = ncm::ProgramId::Spl; + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Spl; namespace result {