Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8dfe04701 | ||
|
|
95d5375158 | ||
|
|
b735bc53c4 | ||
|
|
b4856a2d07 | ||
|
|
ed4491a24f | ||
|
|
93004be59e | ||
|
|
237b513408 | ||
|
|
4c5e980e07 | ||
|
|
08d9de6907 | ||
|
|
6eee3f5fe7 | ||
|
|
4eb3109c93 | ||
|
|
f7fb689412 | ||
|
|
2181adb82b | ||
|
|
40c6733de3 | ||
|
|
c703be86fc | ||
|
|
f3732c72dc | ||
|
|
c7026b9094 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -64,6 +64,7 @@ dkms.conf
|
||||
# Distribution files
|
||||
*.tgz
|
||||
*.zip
|
||||
*.bz2
|
||||
|
||||
# IDA binaries
|
||||
*.id0
|
||||
|
||||
@@ -18,3 +18,5 @@ enable_user_pmu_access = 0
|
||||
[stratosphere]
|
||||
; To force-enable nogc, add nogc = 1
|
||||
; To force-disable nogc, add nogc = 0
|
||||
|
||||
; To opt in to using Atmosphere's NCM reimplementation, add enable_ncm = 1
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
# Changelog
|
||||
## 0.10.4
|
||||
+ With major thanks to @Adubbz for his work, the NCM system module has now been re-implemented.
|
||||
+ This was a major stepping stone towards the goal of having implementations everything in the Switch's package1/package2 firmware.
|
||||
+ This also lays the groundwork for libstratosphere providing bindings for changing the installed version of the Switch's OS.
|
||||
+ **Please Note**: The NCM implementation will initially be opt-in.
|
||||
+ The Atmosphere team is confident in our NCM implementation (and we have tested it on every firmware version).
|
||||
+ That said, this is our first system module that manages NAND savegames -- and caution is a habit.
|
||||
+ We do not anticipate any issues that didn't come up in testing, so this is just our being particularly careful.
|
||||
+ Users interested in opting in to using our implementation should set `stratosphere!ncm_enabled = 1` in BCT.ini.
|
||||
+ In the unlikely event that any issues are encountered, please report them to @SciresM.
|
||||
+ The NCM implementation will stop being opt-in in a future update, after thorough testing has been done in practice.
|
||||
+ A bug was fixed in emummc that caused Nintendo path to be corrupted on 1.0.0.
|
||||
+ This manifested as the emummc folder being created inside the virtual NAND instead of the SD card.
|
||||
+ It's unlikely there are any negative consequences to this in practice.
|
||||
+ If you want to be truly sure, you can re-clone sysmmc before updating a 1.0.0 emummc to latest firmware.
|
||||
+ Stratosphere system modules now use new Nintendo-style FS bindings instead of stdio.
|
||||
+ This saves a modest amount of memory due to leaner code, and greatly increases the accuracy of several components.
|
||||
+ These bindings will make it easier for other system modules using libstratosphere to interact with the filesystem.
|
||||
+ This also lays the groundwork for changes necessary to support per-emummc Atmosphere folders in a future update.
|
||||
+ Atmosphere's fatal error context now dumps 0x100 of TLS.
|
||||
+ This will make it much easier to fix bugs when an error report is dumped for whatever caused the crash.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.10.3
|
||||
+ Support was added for 9.2.0.
|
||||
+ Support was added for redirecting manual html content for games.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}, \
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
|
||||
static inline void uart_wait_cycles(uint32_t baud, uint32_t num)
|
||||
{
|
||||
wait((num * 1000000 + 16 * baud - 1) / (16 * baud));
|
||||
udelay((num * 1000000 + 16 * baud - 1) / (16 * baud));
|
||||
}
|
||||
|
||||
static inline void uart_wait_syms(uint32_t baud, uint32_t num)
|
||||
{
|
||||
wait((num * 1000000 + baud - 1) / baud);
|
||||
udelay((num * 1000000 + baud - 1) / baud);
|
||||
}
|
||||
|
||||
void uart_config(UartDevice dev) {
|
||||
@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
|
||||
|
||||
switch (dev) {
|
||||
case UART_A:
|
||||
pinmux->uart1_tx = 0;
|
||||
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart1_rts = 0;
|
||||
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart1_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_UP | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart1_rts = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_B:
|
||||
pinmux->uart2_tx = 0;
|
||||
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart2_rts = 0;
|
||||
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart2_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart2_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_C:
|
||||
pinmux->uart3_tx = 0;
|
||||
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart3_rts = 0;
|
||||
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart3_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart3_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_D:
|
||||
pinmux->uart4_tx = 0;
|
||||
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart4_rts = 0;
|
||||
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart4_tx = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart4_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_E:
|
||||
/* Unused. */
|
||||
|
||||
@@ -21,66 +21,58 @@
|
||||
#include "gpio.h"
|
||||
#include "utils.h"
|
||||
|
||||
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
|
||||
/* Set GPIO's value. */
|
||||
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
|
||||
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
|
||||
|
||||
return &gpio->bank[bank_number];
|
||||
}
|
||||
|
||||
static volatile uint32_t gpio_get_port(uint32_t pin) {
|
||||
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||
}
|
||||
|
||||
static volatile uint32_t gpio_get_mask(uint32_t pin) {
|
||||
uint32_t pin_number = (pin & GPIO_PIN_MASK);
|
||||
return (1 << pin_number);
|
||||
}
|
||||
|
||||
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
|
||||
|
||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
||||
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||
|
||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||
uint32_t port = gpio_get_port(pin);
|
||||
uint32_t mask = gpio_get_mask(pin);
|
||||
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||
|
||||
/* Set or clear the bit, as appropriate. */
|
||||
if (should_be_set)
|
||||
if (do_set)
|
||||
cluster[port] |= mask;
|
||||
else
|
||||
cluster[port] &= ~mask;
|
||||
|
||||
/* Dummy read. */
|
||||
(void)cluster[port];
|
||||
cluster[port];
|
||||
}
|
||||
|
||||
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
|
||||
/* Get GPIO's value. */
|
||||
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
|
||||
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||
|
||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
||||
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||
|
||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||
uint32_t port = gpio_get_port(pin);
|
||||
uint32_t mask = gpio_get_mask(pin);
|
||||
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||
|
||||
/* Convert the given value to a boolean. */
|
||||
return !!(cluster[port] & mask);
|
||||
}
|
||||
|
||||
/* Configure GPIO's mode. */
|
||||
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
|
||||
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
||||
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
||||
}
|
||||
|
||||
/* Configure GPIO's direction. */
|
||||
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
|
||||
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
||||
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
||||
}
|
||||
|
||||
/* Write to GPIO. */
|
||||
void gpio_write(uint32_t pin, uint32_t value) {
|
||||
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
||||
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
||||
}
|
||||
|
||||
/* Read from GPIO. */
|
||||
uint32_t gpio_read(uint32_t pin) {
|
||||
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
||||
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "panic.h"
|
||||
#include "di.h"
|
||||
@@ -51,8 +51,10 @@ static const char *get_error_desc_str(uint32_t error_desc) {
|
||||
|
||||
static void _check_and_display_atmosphere_fatal_error(void) {
|
||||
/* Check for valid magic. */
|
||||
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
|
||||
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0) {
|
||||
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
|
||||
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 &&
|
||||
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,10 +71,10 @@ static void _check_and_display_atmosphere_fatal_error(void) {
|
||||
/* Turn on the backlight after initializing the lfb */
|
||||
/* to avoid flickering. */
|
||||
display_backlight(true);
|
||||
|
||||
|
||||
/* Override the global logging level. */
|
||||
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
|
||||
|
||||
|
||||
/* Copy fatal error context to the stack. */
|
||||
atmosphere_fatal_error_ctx ctx = *(ATMOSPHERE_FATAL_ERROR_CONTEXT);
|
||||
|
||||
@@ -103,7 +105,7 @@ static void _check_and_display_atmosphere_fatal_error(void) {
|
||||
void check_and_display_panic(void) {
|
||||
/* Handle a panic sent via a stratosphere module. */
|
||||
_check_and_display_atmosphere_fatal_error();
|
||||
|
||||
|
||||
/* We also handle our own panics. */
|
||||
bool has_panic = ((APBDEV_PMC_RST_STATUS_0 != 0) || (g_panic_code != 0));
|
||||
uint32_t code = (g_panic_code == 0) ? APBDEV_PMC_SCRATCH200_0 : g_panic_code;
|
||||
@@ -152,10 +154,10 @@ void check_and_display_panic(void) {
|
||||
|
||||
/* Initialize the display. */
|
||||
display_init();
|
||||
|
||||
|
||||
/* Fill the screen. */
|
||||
display_color_screen(color);
|
||||
|
||||
|
||||
/* Wait for button and reboot. */
|
||||
wait_for_button_and_reboot();
|
||||
} else {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FUSEE_PANIC_H
|
||||
#define FUSEE_PANIC_H
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
|
||||
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
|
||||
#define AMS_FATAL_ERROR_TLS_SIZE 0x100
|
||||
|
||||
/* Atmosphere reboot-to-fatal-error. */
|
||||
typedef struct {
|
||||
@@ -57,10 +58,13 @@ typedef struct {
|
||||
uint64_t stack_dump_size;
|
||||
uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
|
||||
uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
|
||||
uint8_t tls[AMS_FATAL_ERROR_TLS_SIZE];
|
||||
} atmosphere_fatal_error_ctx;
|
||||
|
||||
/* "AFE2" */
|
||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x32454641
|
||||
/* "AFE1" */
|
||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x31454641
|
||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 0x31454641
|
||||
/* "AFE0" */
|
||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641
|
||||
|
||||
|
||||
@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
|
||||
|
||||
switch (dev) {
|
||||
case UART_A:
|
||||
pinmux->uart1_tx = 0;
|
||||
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart1_rts = 0;
|
||||
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart1_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_UP | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart1_rts = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_B:
|
||||
pinmux->uart2_tx = 0;
|
||||
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart2_rts = 0;
|
||||
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart2_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart2_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_C:
|
||||
pinmux->uart3_tx = 0;
|
||||
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart3_rts = 0;
|
||||
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart3_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart3_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_D:
|
||||
pinmux->uart4_tx = 0;
|
||||
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
||||
pinmux->uart4_rts = 0;
|
||||
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
||||
pinmux->uart4_tx = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart4_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||
break;
|
||||
case UART_E:
|
||||
/* Unused. */
|
||||
|
||||
@@ -84,7 +84,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm
|
||||
export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/ncm $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \
|
||||
@@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
|
||||
KIPFILES := loader.kip ncm.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
|
||||
exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \
|
||||
sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \
|
||||
@@ -221,12 +221,12 @@ sept_secondary_01.enc.o sept_secondary_01_enc.h: sept-secondary_01.enc
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(_bin2o)
|
||||
|
||||
|
||||
sept_secondary_dev_00.enc.o sept_secondary_dev_00_enc.h: sept-secondary_dev_00.enc
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(_bin2o)
|
||||
|
||||
|
||||
sept_secondary_dev_01.enc.o sept_secondary_dev_01_enc.h: sept-secondary_dev_01.enc
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
|
||||
@@ -230,6 +230,8 @@ SECTIONS
|
||||
PROVIDE(__loader_kip_size__ = loader_kip_end - loader_kip);
|
||||
PROVIDE(__lp0fw_bin_start__ = lp0fw_bin - __start__);
|
||||
PROVIDE(__lp0fw_bin_size__ = lp0fw_bin_end - lp0fw_bin);
|
||||
PROVIDE(__ncm_kip_start__ = ncm_kip - __start__);
|
||||
PROVIDE(__ncm_kip_size__ = ncm_kip_end - ncm_kip);
|
||||
PROVIDE(__pm_kip_start__ = pm_kip - __start__);
|
||||
PROVIDE(__pm_kip_size__ = pm_kip_end - pm_kip);
|
||||
PROVIDE(__rebootstub_bin_start__ = rebootstub_bin - __start__);
|
||||
|
||||
@@ -21,66 +21,58 @@
|
||||
#include "gpio.h"
|
||||
#include "utils.h"
|
||||
|
||||
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
|
||||
/* Set GPIO's value. */
|
||||
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
|
||||
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
|
||||
|
||||
return &gpio->bank[bank_number];
|
||||
}
|
||||
|
||||
static volatile uint32_t gpio_get_port(uint32_t pin) {
|
||||
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||
}
|
||||
|
||||
static volatile uint32_t gpio_get_mask(uint32_t pin) {
|
||||
uint32_t pin_number = (pin & GPIO_PIN_MASK);
|
||||
return (1 << pin_number);
|
||||
}
|
||||
|
||||
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
|
||||
|
||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
||||
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||
|
||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||
uint32_t port = gpio_get_port(pin);
|
||||
uint32_t mask = gpio_get_mask(pin);
|
||||
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||
|
||||
/* Set or clear the bit, as appropriate. */
|
||||
if (should_be_set)
|
||||
if (do_set)
|
||||
cluster[port] |= mask;
|
||||
else
|
||||
cluster[port] &= ~mask;
|
||||
|
||||
/* Dummy read. */
|
||||
(void)cluster[port];
|
||||
cluster[port];
|
||||
}
|
||||
|
||||
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
|
||||
/* Get GPIO's value. */
|
||||
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
|
||||
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||
|
||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
||||
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||
|
||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||
uint32_t port = gpio_get_port(pin);
|
||||
uint32_t mask = gpio_get_mask(pin);
|
||||
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||
|
||||
/* Convert the given value to a boolean. */
|
||||
return !!(cluster[port] & mask);
|
||||
}
|
||||
|
||||
/* Configure GPIO's mode. */
|
||||
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
|
||||
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
||||
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
||||
}
|
||||
|
||||
/* Configure GPIO's direction. */
|
||||
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
|
||||
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
||||
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
||||
}
|
||||
|
||||
/* Write to GPIO. */
|
||||
void gpio_write(uint32_t pin, uint32_t value) {
|
||||
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
||||
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
||||
}
|
||||
|
||||
/* Read from GPIO. */
|
||||
uint32_t gpio_read(uint32_t pin) {
|
||||
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
||||
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
||||
}
|
||||
|
||||
@@ -184,6 +184,12 @@ static int stratosphere_ini_handler(void *user, const char *section, const char
|
||||
strat_cfg->has_nogc_config = true;
|
||||
sscanf(value, "%d", &tmp);
|
||||
strat_cfg->enable_nogc = tmp != 0;
|
||||
} else if (strcmp(name, STRATOSPHERE_ENABLE_NCM_KEY) == 0) {
|
||||
sscanf(value, "%d", &tmp);
|
||||
strat_cfg->ncm_enabled = tmp != 0;
|
||||
if (strat_cfg->ncm_enabled) {
|
||||
stratosphere_enable_ncm();
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -97,11 +97,18 @@ _metadata:
|
||||
#define CONTENT_TYPE_KLD 9
|
||||
#define CONTENT_TYPE_KRN 10
|
||||
|
||||
#define CONTENT_FLAG_NONE (0 << 0)
|
||||
|
||||
#define CONTENT_FLAG0_EXPERIMENTAL (1 << 0)
|
||||
|
||||
_content_headers:
|
||||
/* ams_mitm content header */
|
||||
.word __ams_mitm_kip_start__
|
||||
.word __ams_mitm_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.byte CONTENT_TYPE_KIP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "ams_mitm"
|
||||
.align 5
|
||||
@@ -109,7 +116,10 @@ _content_headers:
|
||||
/* boot content header */
|
||||
.word __boot_kip_start__
|
||||
.word __boot_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.byte CONTENT_TYPE_KIP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "boot"
|
||||
.align 5
|
||||
@@ -117,7 +127,10 @@ _content_headers:
|
||||
/* exosphere content header */
|
||||
.word __exosphere_bin_start__
|
||||
.word __exosphere_bin_size__
|
||||
.word CONTENT_TYPE_EXO
|
||||
.byte CONTENT_TYPE_EXO
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "exosphere"
|
||||
.align 5
|
||||
@@ -125,7 +138,10 @@ _content_headers:
|
||||
/* fusee_primary content header */
|
||||
.word __fusee_primary_bin_start__
|
||||
.word __fusee_primary_bin_size__
|
||||
.word CONTENT_TYPE_FSP
|
||||
.byte CONTENT_TYPE_FSP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "fusee_primary"
|
||||
.align 5
|
||||
@@ -133,15 +149,21 @@ _content_headers:
|
||||
/* loader content header */
|
||||
.word __loader_kip_start__
|
||||
.word __loader_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.byte CONTENT_TYPE_KIP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "loader"
|
||||
.asciz "Loader"
|
||||
.align 5
|
||||
|
||||
/* lp0fw content header */
|
||||
.word __lp0fw_bin_start__
|
||||
.word __lp0fw_bin_size__
|
||||
.word CONTENT_TYPE_WBT
|
||||
.byte CONTENT_TYPE_WBT
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "lp0fw"
|
||||
.align 5
|
||||
@@ -149,15 +171,21 @@ _content_headers:
|
||||
/* pm content header */
|
||||
.word __pm_kip_start__
|
||||
.word __pm_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.byte CONTENT_TYPE_KIP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "pm"
|
||||
.asciz "ProcessManager"
|
||||
.align 5
|
||||
|
||||
/* rebootstub content header */
|
||||
.word __rebootstub_bin_start__
|
||||
.word __rebootstub_bin_size__
|
||||
.word CONTENT_TYPE_RBT
|
||||
.byte CONTENT_TYPE_RBT
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "rebootstub"
|
||||
.align 5
|
||||
@@ -165,7 +193,10 @@ _content_headers:
|
||||
/* sept_primary content header */
|
||||
.word __sept_primary_bin_start__
|
||||
.word __sept_primary_bin_size__
|
||||
.word CONTENT_TYPE_SP1
|
||||
.byte CONTENT_TYPE_SP1
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "sept_primary"
|
||||
.align 5
|
||||
@@ -173,7 +204,10 @@ _content_headers:
|
||||
/* sept_secondary 00 content header */
|
||||
.word __sept_secondary_00_enc_start__
|
||||
.word __sept_secondary_00_enc_size__
|
||||
.word CONTENT_TYPE_SP2
|
||||
.byte CONTENT_TYPE_SP2
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "septsecondary00"
|
||||
.align 5
|
||||
@@ -181,7 +215,10 @@ _content_headers:
|
||||
/* sept_secondary 01 content header */
|
||||
.word __sept_secondary_01_enc_start__
|
||||
.word __sept_secondary_01_enc_size__
|
||||
.word CONTENT_TYPE_SP2
|
||||
.byte CONTENT_TYPE_SP2
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "septsecondary01"
|
||||
.align 5
|
||||
@@ -189,7 +226,10 @@ _content_headers:
|
||||
/* sm content header */
|
||||
.word __sm_kip_start__
|
||||
.word __sm_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.byte CONTENT_TYPE_KIP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "sm"
|
||||
.align 5
|
||||
@@ -197,15 +237,32 @@ _content_headers:
|
||||
/* spl content header */
|
||||
.word __spl_kip_start__
|
||||
.word __spl_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.byte CONTENT_TYPE_KIP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "spl"
|
||||
.align 5
|
||||
|
||||
/* ncm content header */
|
||||
.word __ncm_kip_start__
|
||||
.word __ncm_kip_size__
|
||||
.byte CONTENT_TYPE_KIP
|
||||
.byte CONTENT_FLAG0_EXPERIMENTAL
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "NCM"
|
||||
.align 5
|
||||
|
||||
/* emummc content header */
|
||||
.word __emummc_kip_start__
|
||||
.word __emummc_kip_size__
|
||||
.word CONTENT_TYPE_EMC
|
||||
.byte CONTENT_TYPE_EMC
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "emummc"
|
||||
.align 5
|
||||
@@ -213,7 +270,10 @@ _content_headers:
|
||||
/* kernel_ldr content header */
|
||||
.word __kernel_ldr_bin_start__
|
||||
.word __kernel_ldr_bin_size__
|
||||
.word CONTENT_TYPE_KLD
|
||||
.byte CONTENT_TYPE_KLD
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "kernel_ldr"
|
||||
.align 5
|
||||
@@ -221,7 +281,10 @@ _content_headers:
|
||||
/* splash_screen content header */
|
||||
.word __splash_screen_bmp_start__
|
||||
.word __splash_screen_bmp_size__
|
||||
.word CONTENT_TYPE_BMP
|
||||
.byte CONTENT_TYPE_BMP
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.byte CONTENT_FLAG_NONE
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "splash_screen"
|
||||
.align 5
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
@@ -30,6 +30,7 @@
|
||||
#define u8 uint8_t
|
||||
#define u32 uint32_t
|
||||
#include "loader_kip.h"
|
||||
#include "ncm_kip.h"
|
||||
#include "pm_kip.h"
|
||||
#include "sm_kip.h"
|
||||
#include "ams_mitm_kip.h"
|
||||
@@ -47,9 +48,10 @@ static bool g_stratosphere_pm_enabled = true;
|
||||
static bool g_stratosphere_ams_mitm_enabled = true;
|
||||
static bool g_stratosphere_spl_enabled = true;
|
||||
static bool g_stratosphere_boot_enabled = true;
|
||||
static bool g_stratosphere_ncm_enabled = false;
|
||||
|
||||
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
|
||||
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ams_mitm_kip_size;
|
||||
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ncm_kip[], ams_mitm_kip[];
|
||||
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ncm_kip_size, ams_mitm_kip_size;
|
||||
|
||||
static emummc_fs_ver_t g_fs_ver = FS_VER_1_0_0;
|
||||
|
||||
@@ -57,6 +59,19 @@ emummc_fs_ver_t stratosphere_get_fs_version(void) {
|
||||
return g_fs_ver;
|
||||
}
|
||||
|
||||
void stratosphere_enable_ncm(void) {
|
||||
/* The Atmosphere team believes our implementation of NCM to be extremely accurate, */
|
||||
/* and does not think it likely there is any possibility of undesirable behavior */
|
||||
/* when using the NCM reimplementation. However, because NCM manages critical save games */
|
||||
/* the implementation will default to off for some time, until the code has been thoroughly */
|
||||
/* tested in practice. */
|
||||
|
||||
/* PLEASE NOTE: The default behavior will be NCM on in a future atmosphere release, */
|
||||
/* and this opt-in functionality will be removed at that time. */
|
||||
|
||||
g_stratosphere_ncm_enabled = true;
|
||||
}
|
||||
|
||||
/* GCC doesn't consider the size as const... we have to write it ourselves. */
|
||||
|
||||
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
@@ -68,38 +83,43 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
}
|
||||
|
||||
size_t size = sizeof(ini1_header_t);
|
||||
|
||||
|
||||
/* Calculate our processes' sizes. */
|
||||
if (g_stratosphere_loader_enabled) {
|
||||
size += loader_kip_size;
|
||||
num_processes++;
|
||||
}
|
||||
|
||||
|
||||
if (g_stratosphere_pm_enabled) {
|
||||
size += pm_kip_size;
|
||||
num_processes++;
|
||||
}
|
||||
|
||||
|
||||
if (g_stratosphere_sm_enabled) {
|
||||
size += sm_kip_size;
|
||||
num_processes++;
|
||||
}
|
||||
|
||||
|
||||
if (g_stratosphere_spl_enabled) {
|
||||
size += spl_kip_size;
|
||||
num_processes++;
|
||||
}
|
||||
|
||||
|
||||
if (g_stratosphere_ams_mitm_enabled) {
|
||||
size += ams_mitm_kip_size;
|
||||
num_processes++;
|
||||
}
|
||||
|
||||
|
||||
if (g_stratosphere_ncm_enabled) {
|
||||
size += ncm_kip_size;
|
||||
num_processes++;
|
||||
}
|
||||
|
||||
if (g_stratosphere_boot_enabled) {
|
||||
size += boot_kip_size;
|
||||
num_processes++;
|
||||
}
|
||||
|
||||
|
||||
g_stratosphere_ini1 = (ini1_header_t *)malloc(size);
|
||||
|
||||
if (g_stratosphere_ini1 == NULL) {
|
||||
@@ -134,6 +154,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
data += spl_kip_size;
|
||||
}
|
||||
|
||||
if (g_stratosphere_ncm_enabled) {
|
||||
memcpy(data, ncm_kip, ncm_kip_size);
|
||||
data += ncm_kip_size;
|
||||
}
|
||||
|
||||
if (g_stratosphere_ams_mitm_enabled) {
|
||||
memcpy(data, ams_mitm_kip, ams_mitm_kip_size);
|
||||
data += ams_mitm_kip_size;
|
||||
@@ -143,7 +168,7 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
memcpy(data, boot_kip, boot_kip_size);
|
||||
data += boot_kip_size;
|
||||
}
|
||||
|
||||
|
||||
return g_stratosphere_ini1;
|
||||
}
|
||||
|
||||
@@ -160,26 +185,26 @@ void stratosphere_free_ini1(void) {
|
||||
|
||||
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
||||
size_t file_size = get_file_size(kip_path);
|
||||
|
||||
|
||||
if (ini1->size + file_size > PACKAGE2_SIZE_MAX) {
|
||||
fatal_error("Failed to load %s: INI1 would be too large!\n", kip_path);
|
||||
}
|
||||
|
||||
|
||||
kip1_header_t kip_header;
|
||||
if (read_from_file(&kip_header, sizeof(kip_header), kip_path) != sizeof(kip_header) || kip_header.magic != MAGIC_KIP1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
size_t kip_size = kip1_get_size_from_header(&kip_header);
|
||||
if (kip_size > file_size) {
|
||||
fatal_error("Failed to load %s: KIP size is corrupt!\n", kip_path);
|
||||
}
|
||||
|
||||
|
||||
if (read_from_file(ini1->kip_data + ini1->size - sizeof(ini1_header_t), kip_size, kip_path) != kip_size) {
|
||||
/* TODO: is this error fatal? */
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ini1->size += kip_size;
|
||||
ini1->num_processes++;
|
||||
}
|
||||
@@ -189,24 +214,24 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
|
||||
size_t fs_kip_size, emummc_kip_size;
|
||||
fs_kip = kip1_uncompress(fs_kip, &fs_kip_size);
|
||||
emummc_kip = kip1_uncompress(emummc_kip, &emummc_kip_size);
|
||||
|
||||
|
||||
/* Allocate kip. */
|
||||
kip1_header_t *injected_kip = calloc(1, fs_kip_size + emummc_kip_size);
|
||||
if (injected_kip == NULL) {
|
||||
fatal_error("Failed to allocate memory for emummc kip injection!");
|
||||
}
|
||||
memcpy(injected_kip, fs_kip, sizeof(*fs_kip));
|
||||
|
||||
|
||||
const size_t fs_contents_size = kip1_get_size_from_header(fs_kip) - sizeof(kip1_header_t);
|
||||
|
||||
|
||||
const size_t emummc_data_size = emummc_kip->section_headers[3].out_offset + emummc_kip->section_headers[3].out_size;
|
||||
if (emummc_data_size & 0xFFF) {
|
||||
fatal_error("Invalid emummc kip!");
|
||||
}
|
||||
|
||||
|
||||
/* Copy over capabilities. */
|
||||
memcpy(injected_kip->capabilities, emummc_kip->capabilities, sizeof(emummc_kip->capabilities));
|
||||
|
||||
|
||||
/* Add extra cap for 1.0.0 */
|
||||
if (stratosphere_get_fs_version() == FS_VER_1_0_0) {
|
||||
for (size_t i = 0; i < 0x20; i++) {
|
||||
@@ -217,14 +242,14 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Update sections. */
|
||||
injected_kip->section_headers[0].out_size += emummc_data_size;
|
||||
injected_kip->section_headers[0].compressed_size += emummc_data_size;
|
||||
for (size_t i = 1; i < 4; i++) {
|
||||
injected_kip->section_headers[i].out_offset += emummc_data_size;
|
||||
}
|
||||
|
||||
|
||||
/* Copy data. */
|
||||
{
|
||||
size_t ofs = 0;
|
||||
@@ -234,22 +259,22 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
|
||||
}
|
||||
}
|
||||
memcpy(injected_kip->data + emummc_data_size, fs_kip->data, fs_contents_size);
|
||||
|
||||
|
||||
return injected_kip;
|
||||
}
|
||||
|
||||
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||
if (g_sd_files_ini1 != NULL) {
|
||||
return g_sd_files_ini1;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate space. */
|
||||
g_sd_files_ini1 = (ini1_header_t *)malloc(PACKAGE2_SIZE_MAX);
|
||||
g_sd_files_ini1->magic = MAGIC_INI1;
|
||||
g_sd_files_ini1->size = sizeof(ini1_header_t);
|
||||
g_sd_files_ini1->num_processes = 0;
|
||||
g_sd_files_ini1->_0xC = 0;
|
||||
|
||||
|
||||
/* Load all kips from /atmosphere/kips/<*>.kip or /atmosphere/kips/<*>/<*>.kip. */
|
||||
DIR *kips_dir = opendir("atmosphere/kips");
|
||||
struct dirent *ent;
|
||||
@@ -258,15 +283,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
char kip_path[0x301] = {0};
|
||||
snprintf(kip_path, 0x300, "atmosphere/kips/%s", ent->d_name);
|
||||
|
||||
|
||||
struct stat kip_stat;
|
||||
if (stat(kip_path, &kip_stat) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
|
||||
/* If file, add to ini1. */
|
||||
try_add_sd_kip(g_sd_files_ini1, kip_path);
|
||||
@@ -279,15 +304,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||
if (strcmp(sub_ent->d_name, ".") == 0 || strcmp(sub_ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* Reuse kip path variable. */
|
||||
memset(kip_path, 0, sizeof(kip_path));
|
||||
snprintf(kip_path, 0x300, "atmosphere/kips/%s/%s", ent->d_name, sub_ent->d_name);
|
||||
|
||||
|
||||
if (stat(kip_path, &kip_stat) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
|
||||
/* If file, add to ini1. */
|
||||
try_add_sd_kip(g_sd_files_ini1, kip_path);
|
||||
@@ -299,7 +324,7 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||
}
|
||||
closedir(kips_dir);
|
||||
}
|
||||
|
||||
|
||||
return g_sd_files_ini1;
|
||||
}
|
||||
|
||||
@@ -344,7 +369,7 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
|
||||
}
|
||||
|
||||
offset += kip1_get_size_from_header(current_kip);
|
||||
|
||||
|
||||
bool already_loaded = false;
|
||||
for (uint32_t j = 0; j < merged->num_processes; j++) {
|
||||
if (process_list[j] == current_kip->title_id) {
|
||||
@@ -355,20 +380,20 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
|
||||
if (already_loaded) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loading KIP %08x%08x...\n", (uint32_t)(current_kip->title_id >> 32), (uint32_t)current_kip->title_id);
|
||||
|
||||
size_t current_kip_size = kip1_get_size_from_header(current_kip);
|
||||
if (current_kip_size > remaining_size) {
|
||||
fatal_error("Not enough space for all the KIP1s!\n");
|
||||
}
|
||||
|
||||
|
||||
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size, &g_fs_ver);
|
||||
|
||||
|
||||
if (current_kip->title_id == FS_TITLE_ID && emummc != NULL) {
|
||||
patched_kip = inject_emummc_kip(patched_kip != NULL ? patched_kip : current_kip, (kip1_header_t *)emummc);
|
||||
}
|
||||
|
||||
|
||||
if (patched_kip != NULL) {
|
||||
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
|
||||
if (patched_kip_size > remaining_size) {
|
||||
@@ -377,9 +402,9 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
|
||||
memcpy(current_dst_kip, patched_kip, patched_kip_size);
|
||||
remaining_size -= patched_kip_size;
|
||||
current_dst_kip += patched_kip_size;
|
||||
|
||||
|
||||
free(patched_kip);
|
||||
} else {
|
||||
} else {
|
||||
memcpy(current_dst_kip, current_kip, current_kip_size);
|
||||
remaining_size -= current_kip_size;
|
||||
current_dst_kip += current_kip_size;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FUSEE_STRATOSPHERE_H
|
||||
#define FUSEE_STRATOSPHERE_H
|
||||
|
||||
@@ -30,6 +30,8 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware);
|
||||
ini1_header_t *stratosphere_get_sd_files_ini1(void);
|
||||
void stratosphere_free_ini1(void);
|
||||
|
||||
void stratosphere_enable_ncm(void);
|
||||
|
||||
emummc_fs_ver_t stratosphere_get_fs_version(void);
|
||||
|
||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis, void *emummc, size_t emummc_size);
|
||||
@@ -37,8 +39,10 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_in
|
||||
typedef struct {
|
||||
bool has_nogc_config;
|
||||
bool enable_nogc;
|
||||
bool ncm_enabled;
|
||||
} stratosphere_cfg_t;
|
||||
|
||||
#define STRATOSPHERE_NOGC_KEY "nogc"
|
||||
#define STRATOSPHERE_ENABLE_NCM_KEY "enable_ncm"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 83aa6133ee2eb43287f8fc373d309a0c99337429
|
||||
parent = fd34e2342a66b2aa3062ea7cecaf9728f12ef21a
|
||||
commit = a4ce117292cc86e951d82666f4e13bc1dc40443f
|
||||
parent = 95d5375158f6df5376ce876e6ed8c22150ad88ff
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -15,11 +15,11 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
export DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
|
||||
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
|
||||
export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
||||
export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
|
||||
export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
|
||||
export SETTINGS = $(ATMOSPHERE_SETTINGS) -O2
|
||||
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
||||
export ASFLAGS = $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
|
||||
|
||||
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
||||
-Wl,--wrap,__cxa_throw \
|
||||
@@ -36,17 +36,17 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
||||
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
|
||||
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
|
||||
|
||||
export LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
|
||||
export LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
export LIBS := -lstratosphere -lnx
|
||||
export LIBS = -lstratosphere -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
export LIBDIRS := $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
|
||||
export LIBDIRS = $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# stratosphere sysmodules may (but usually do not) have an exefs source dir
|
||||
#---------------------------------------------------------------------------------
|
||||
export EXEFS_SRC := exefs_src
|
||||
export EXEFS_SRC = exefs_src
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<u8>(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit));
|
||||
R_UNLESS(num_reserved != num_desired, ResultSuccess());
|
||||
R_SUCCEED_IF(num_reserved == num_desired);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -83,12 +83,13 @@ namespace ams {
|
||||
struct FatalErrorContext : sf::LargeData, sf::PrefersMapAliasTransferMode {
|
||||
static constexpr size_t MaxStackTrace = 0x20;
|
||||
static constexpr size_t MaxStackDumpSize = 0x100;
|
||||
static constexpr size_t ThreadLocalSize = 0x100;
|
||||
static constexpr size_t NumGprs = 29;
|
||||
static constexpr uintptr_t StdAbortMagicAddress = 0x8;
|
||||
static constexpr u64 StdAbortMagicValue = 0xA55AF00DDEADCAFEul;
|
||||
static constexpr u32 StdAbortErrorDesc = 0xFFE;
|
||||
static constexpr u32 DataAbortErrorDesc = 0x101;
|
||||
static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '1'>::Code;
|
||||
static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '2'>::Code;
|
||||
|
||||
u32 magic;
|
||||
u32 error_desc;
|
||||
@@ -113,10 +114,11 @@ namespace ams {
|
||||
u64 stack_trace_size;
|
||||
u64 stack_dump_size;
|
||||
u64 stack_trace[MaxStackTrace];
|
||||
u8 stack_dump[MaxStackDumpSize];
|
||||
u8 stack_dump[MaxStackDumpSize];
|
||||
u8 tls[ThreadLocalSize];
|
||||
};
|
||||
|
||||
static_assert(sizeof(FatalErrorContext) == 0x350, "sizeof(FatalErrorContext)");
|
||||
static_assert(sizeof(FatalErrorContext) == 0x450, "sizeof(FatalErrorContext)");
|
||||
static_assert(std::is_pod<FatalErrorContext>::value, "FatalErrorContext");
|
||||
|
||||
#ifdef ATMOSPHERE_GIT_BRANCH
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../os/os_common_types.hpp"
|
||||
#include "../ncm/ncm_types.hpp"
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
|
||||
namespace ams::cfg {
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../os/os_common_types.hpp"
|
||||
#include "../ncm/ncm_types.hpp"
|
||||
#include "../sf/sf_buffer_tags.hpp"
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||
|
||||
namespace ams::dmnt::cheat {
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "../ncm/ncm_types.hpp"
|
||||
#include "../sf/sf_buffer_tags.hpp"
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||
|
||||
namespace ams::fatal {
|
||||
|
||||
|
||||
@@ -19,11 +19,33 @@
|
||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||
#include <stratosphere/fs/impl/fs_filesystem_proxy_type.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_registrar.hpp>
|
||||
#include <stratosphere/fs/fs_remote_filesystem.hpp>
|
||||
#include <stratosphere/fs/fs_readonly_filesystem_adapter.hpp>
|
||||
#include <stratosphere/fs/fs_read_only_filesystem.hpp>
|
||||
#include <stratosphere/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/fs_substorage.hpp>
|
||||
#include <stratosphere/fs/fs_memory_storage.hpp>
|
||||
#include <stratosphere/fs/fs_remote_storage.hpp>
|
||||
#include <stratosphere/fs/fs_file_storage.hpp>
|
||||
#include <stratosphere/fs/fs_query_range.hpp>
|
||||
#include <stratosphere/fs/impl/fs_common_mount_name.hpp>
|
||||
#include <stratosphere/fs/fs_mount.hpp>
|
||||
#include <stratosphere/fs/fs_path_tool.hpp>
|
||||
#include <stratosphere/fs/fs_path_utils.hpp>
|
||||
#include <stratosphere/fs/fs_filesystem_utils.hpp>
|
||||
#include <stratosphere/fs/fs_romfs_filesystem.hpp>
|
||||
#include <stratosphere/fs/impl/fs_data.hpp>
|
||||
#include <stratosphere/fs/fs_application.hpp>
|
||||
#include <stratosphere/fs/fs_bis.hpp>
|
||||
#include <stratosphere/fs/fs_code.hpp>
|
||||
#include <stratosphere/fs/fs_content.hpp>
|
||||
#include <stratosphere/fs/fs_content_storage.hpp>
|
||||
#include <stratosphere/fs/fs_game_card.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_types.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_management.hpp>
|
||||
#include <stratosphere/fs/fs_save_data_transaction.hpp>
|
||||
#include <stratosphere/fs/fs_sd_card.hpp>
|
||||
#include <stratosphere/fs/fs_signed_system_partition.hpp>
|
||||
#include <stratosphere/fs/fs_system_data.hpp>
|
||||
#include <stratosphere/fs/fs_system_save_data.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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result MountApplicationPackage(const char *name, const char *common_path);
|
||||
|
||||
}
|
||||
58
libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp
Normal file
58
libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/fs_istorage.hpp>
|
||||
|
||||
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<fs::IStorage> *out, BisPartitionId id);
|
||||
|
||||
Result InvalidateBisCache();
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result MountCode(const char *name, const char *path, ncm::ProgramId program_id);
|
||||
|
||||
Result MountCodeForAtmosphereWithRedirection(const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific);
|
||||
Result MountCodeForAtmosphere(const char *name, const char *path, ncm::ProgramId program_id);
|
||||
|
||||
}
|
||||
@@ -15,13 +15,34 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "../os.hpp"
|
||||
#include "../ncm.hpp"
|
||||
#include "../sf.hpp"
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
#include <stratosphere/sf.hpp>
|
||||
|
||||
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<u32>((v & static_cast<u64>(0x00000000FFFFFFFFul)) >> 0);
|
||||
this->high = static_cast<u32>((v & static_cast<u64>(0xFFFFFFFF00000000ul)) >> 32);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s64 Get() const {
|
||||
return (static_cast<s64>(this->high) << 32) | (static_cast<s64>(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<Int64>::value);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<FindPosition>::value);
|
||||
|
||||
using DirectoryInfo = RomDirectoryInfo;
|
||||
using FileInfo = RomFileInfo;
|
||||
|
||||
static constexpr RomFileId ConvertToFileId(Position pos) {
|
||||
return static_cast<RomFileId>(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<RomDirectoryId>(pos);
|
||||
}
|
||||
|
||||
static constexpr Position ConvertToPosition(RomDirectoryId id) {
|
||||
return static_cast<Position>(id);
|
||||
}
|
||||
|
||||
static_assert(std::is_same<RomDirectoryId, RomFileId>::value);
|
||||
|
||||
struct RomDirectoryEntry {
|
||||
Position next;
|
||||
Position dir;
|
||||
Position file;
|
||||
};
|
||||
static_assert(std::is_pod<RomDirectoryEntry>::value);
|
||||
|
||||
struct RomFileEntry {
|
||||
Position next;
|
||||
FileInfo info;
|
||||
};
|
||||
static_assert(std::is_pod<RomFileEntry>::value);
|
||||
|
||||
static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength;
|
||||
|
||||
template<typename ImplKeyType, typename ClientKeyType, typename ValueType>
|
||||
class EntryMapTable : public RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength> {
|
||||
public:
|
||||
using ImplKey = ImplKeyType;
|
||||
using ClientKey = ClientKeyType;
|
||||
using Value = ValueType;
|
||||
using Position = HierarchicalRomFileTable::Position;
|
||||
using Base = RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength>;
|
||||
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<const RomPathChar *>(aux_lhs), reinterpret_cast<const RomPathChar *>(aux_rhs), aux_lhs_size / sizeof(RomPathChar));
|
||||
}
|
||||
};
|
||||
static_assert(std::is_pod<RomEntryKey>::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<u32>(static_cast<std::make_unsigned<RomPathChar>::type>(*(name++)));
|
||||
hash = ((hash >> 5) | (hash << 27)) ^ cur;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
static_assert(std::is_pod<EntryKey>::value);
|
||||
|
||||
using DirectoryEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomDirectoryEntry>;
|
||||
using FileEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomFileEntry>;
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_dbm_rom_types.hpp"
|
||||
#include "fs_substorage.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
template<typename KeyType, typename ValueType, size_t MaxAuxiliarySize>
|
||||
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<FindIndex>::value);
|
||||
private:
|
||||
static constexpr inline Position InvalidPosition = ~Position();
|
||||
|
||||
struct Element {
|
||||
Key key;
|
||||
Value value;
|
||||
Position next;
|
||||
u32 size;
|
||||
};
|
||||
static_assert(std::is_pod<Element>::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<BucketIndex>(-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<BucketIndex>(-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<u32>(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<u8 *>(::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<size_t>(kv_size), fs::ResultDbmKeyFull());
|
||||
|
||||
*out = static_cast<Position>(this->total_entry_size);
|
||||
|
||||
this->total_entry_size = util::AlignUp(static_cast<s64>(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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<RomEntryName>::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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<RomFileSystemInformation>::value);
|
||||
static_assert(sizeof(RomFileSystemInformation) == 0x50);
|
||||
|
||||
struct RomDirectoryInfo {
|
||||
/* ... */
|
||||
};
|
||||
static_assert(std::is_pod<RomDirectoryInfo>::value);
|
||||
|
||||
struct RomFileInfo {
|
||||
Int64 offset;
|
||||
Int64 size;
|
||||
};
|
||||
static_assert(std::is_pod<RomFileInfo>::value);
|
||||
|
||||
namespace RomStringTraits {
|
||||
|
||||
constexpr inline char DirectorySeparator = '/';
|
||||
constexpr inline char NullTerminator = '\x00';
|
||||
constexpr inline char Dot = '.';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -60,4 +60,19 @@ namespace ams::fs {
|
||||
|
||||
static_assert(std::is_pod<WriteOption>::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);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,13 +18,19 @@
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace fsa {
|
||||
|
||||
class IFile;
|
||||
|
||||
}
|
||||
|
||||
enum OpenMode {
|
||||
OpenMode_Read = ::FsOpenMode_Read,
|
||||
OpenMode_Write = ::FsOpenMode_Write,
|
||||
OpenMode_Append = ::FsOpenMode_Append,
|
||||
OpenMode_Read = (1 << 0),
|
||||
OpenMode_Write = (1 << 1),
|
||||
OpenMode_AllowAppend = (1 << 2),
|
||||
|
||||
OpenMode_ReadWrite = (OpenMode_Read | OpenMode_Write),
|
||||
OpenMode_All = (OpenMode_ReadWrite | OpenMode_Append),
|
||||
OpenMode_All = (OpenMode_ReadWrite | OpenMode_AllowAppend),
|
||||
};
|
||||
|
||||
enum OpenDirectoryMode {
|
||||
@@ -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<fsa::IFile> &&file, int mode);
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include "fs_filesystem.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
/* Common utilities. */
|
||||
Result EnsureDirectoryRecursively(const char *path);
|
||||
Result EnsureParentDirectoryRecursively(const char *path);
|
||||
|
||||
Result HasFile(bool *out, const char *path);
|
||||
Result HasDirectory(bool *out, const char *path);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<typename T>
|
||||
std::unique_ptr<T, Deleter> MakeUnique() {
|
||||
static_assert(std::is_pod<T>::value);
|
||||
return std::unique_ptr<T, Deleter>(static_cast<T *>(::ams::fs::impl::Allocate(sizeof(T))), Deleter(sizeof(T)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<u8 *>(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<QueryRangeInfo *>(dst)->Clear();
|
||||
return ResultSuccess();
|
||||
default:
|
||||
return fs::ResultUnsupportedOperationInMemoryStorageB();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -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]));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
class ReadOnlyFile : public fsa::IFile, public impl::Newable {
|
||||
NON_COPYABLE(ReadOnlyFile);
|
||||
NON_MOVEABLE(ReadOnlyFile);
|
||||
private:
|
||||
std::unique_ptr<fsa::IFile> base_file;
|
||||
public:
|
||||
explicit ReadOnlyFile(std::unique_ptr<fsa::IFile> &&f) : base_file(std::move(f)) { /* ... */ }
|
||||
virtual ~ReadOnlyFile() { /* ... */ }
|
||||
private:
|
||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
||||
return this->base_file->Read(out, offset, buffer, size, option);
|
||||
}
|
||||
|
||||
virtual Result GetSizeImpl(s64 *out) override final {
|
||||
return this->base_file->GetSize(out);
|
||||
}
|
||||
|
||||
virtual Result FlushImpl() override final {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileA();
|
||||
}
|
||||
|
||||
virtual Result SetSizeImpl(s64 size) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileA();
|
||||
}
|
||||
|
||||
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 {
|
||||
switch (op_id) {
|
||||
case OperationId::InvalidateCache:
|
||||
case OperationId::QueryRange:
|
||||
return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
default:
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileB();
|
||||
}
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
return this->base_file->GetDomainObjectId();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class ReadOnlyFileSystemTemplate : public fsa::IFileSystem, public impl::Newable {
|
||||
NON_COPYABLE(ReadOnlyFileSystemTemplate);
|
||||
NON_MOVEABLE(ReadOnlyFileSystemTemplate);
|
||||
private:
|
||||
T base_fs;
|
||||
public:
|
||||
explicit ReadOnlyFileSystemTemplate(T &&fs) : base_fs(std::move(fs)) { /* ... */ }
|
||||
virtual ~ReadOnlyFileSystemTemplate() { /* ... */ }
|
||||
private:
|
||||
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||
/* Only allow opening files with mode = read. */
|
||||
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument());
|
||||
|
||||
std::unique_ptr<fsa::IFile> base_file;
|
||||
R_TRY(this->base_fs->OpenFile(std::addressof(base_file), path, mode));
|
||||
|
||||
auto read_only_file = std::make_unique<ReadOnlyFile>(std::move(base_file));
|
||||
R_UNLESS(read_only_file != nullptr, fs::ResultAllocationFailureInReadOnlyFileSystemA());
|
||||
|
||||
*out_file = std::move(read_only_file);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
||||
return this->base_fs->OpenDirectory(out_dir, path, mode);
|
||||
}
|
||||
|
||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
||||
return this->base_fs->GetEntryType(out, path);
|
||||
}
|
||||
|
||||
virtual Result CommitImpl() override final {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result DeleteFileImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result CreateDirectoryImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||
}
|
||||
|
||||
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB();
|
||||
}
|
||||
|
||||
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB();
|
||||
}
|
||||
|
||||
virtual Result CommitProvisionallyImpl(s64 counter) override final {
|
||||
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateC();
|
||||
}
|
||||
};
|
||||
|
||||
using ReadOnlyFileSystem = ReadOnlyFileSystemTemplate<std::unique_ptr<::ams::fs::fsa::IFileSystem>>;
|
||||
using ReadOnlyFileSystemShared = ReadOnlyFileSystemTemplate<std::shared_ptr<::ams::fs::fsa::IFileSystem>>;
|
||||
|
||||
}
|
||||
@@ -1,152 +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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
class ReadOnlyFileAdapter : public fsa::IFile {
|
||||
NON_COPYABLE(ReadOnlyFileAdapter);
|
||||
private:
|
||||
std::unique_ptr<fsa::IFile> base_file;
|
||||
public:
|
||||
ReadOnlyFileAdapter(fsa::IFile *f) : base_file(f) { /* ... */ }
|
||||
ReadOnlyFileAdapter(std::unique_ptr<fsa::IFile> f) : base_file(std::move(f)) { /* ... */ }
|
||||
virtual ~ReadOnlyFileAdapter() { /* ... */ }
|
||||
public:
|
||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
||||
/* Ensure that we can read these extents. */
|
||||
size_t read_size = 0;
|
||||
R_TRY(this->DryRead(std::addressof(read_size), offset, size, option, fs::OpenMode_Read));
|
||||
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(offset >= 0);
|
||||
AMS_ASSERT(buffer != nullptr || size == 0);
|
||||
|
||||
return this->base_file->Read(out, offset, buffer, size, option);
|
||||
}
|
||||
|
||||
virtual Result GetSizeImpl(s64 *out) override final {
|
||||
return this->base_file->GetSize(out);
|
||||
}
|
||||
|
||||
virtual Result FlushImpl() override final {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSizeImpl(s64 size) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
return this->base_file->GetDomainObjectId();
|
||||
}
|
||||
};
|
||||
|
||||
class ReadOnlyFileSystemAdapter : public fsa::IFileSystem {
|
||||
NON_COPYABLE(ReadOnlyFileSystemAdapter);
|
||||
private:
|
||||
std::shared_ptr<fsa::IFileSystem> shared_fs;
|
||||
std::unique_ptr<fsa::IFileSystem> unique_fs;
|
||||
protected:
|
||||
fsa::IFileSystem * const base_fs;
|
||||
public:
|
||||
template<typename T>
|
||||
explicit ReadOnlyFileSystemAdapter(std::shared_ptr<T> fs) : shared_fs(std::move(fs)), base_fs(shared_fs.get()) { static_assert(std::is_base_of<fsa::IFileSystem, T>::value); }
|
||||
|
||||
template<typename T>
|
||||
explicit ReadOnlyFileSystemAdapter(std::unique_ptr<T> fs) : unique_fs(std::move(fs)), base_fs(unique_fs.get()) { static_assert(std::is_base_of<fsa::IFileSystem, T>::value); }
|
||||
|
||||
virtual ~ReadOnlyFileSystemAdapter() { /* ... */ }
|
||||
public:
|
||||
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result DeleteFileImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result CreateDirectoryImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
||||
return this->base_fs->GetEntryType(out, path);
|
||||
}
|
||||
|
||||
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||
std::unique_ptr<fsa::IFile> f;
|
||||
R_TRY(this->base_fs->OpenFile(std::addressof(f), path, mode));
|
||||
|
||||
*out_file = std::make_unique<ReadOnlyFileAdapter>(std::move(f));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
||||
return this->base_fs->OpenDirectory(out_dir, path, mode);
|
||||
}
|
||||
|
||||
virtual Result CommitImpl() override final {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) {
|
||||
return this->base_fs->GetFileTimeStampRaw(out, path);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -14,165 +14,217 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include "fsa/fs_ifile.hpp"
|
||||
#include "fsa/fs_idirectory.hpp"
|
||||
#include "fsa/fs_ifilesystem.hpp"
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||
#include <stratosphere/fs/fs_query_range.hpp>
|
||||
#include <stratosphere/fs/fs_path_tool.hpp>
|
||||
#include <stratosphere/fs/fs_path_utils.hpp>
|
||||
|
||||
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<fsa::IFile> *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<RemoteFile>(f);
|
||||
FsFile f;
|
||||
R_TRY(fsFsOpenFile(std::addressof(this->base_fs), sf_path.str, mode, &f));
|
||||
|
||||
auto file = std::make_unique<RemoteFile>(f);
|
||||
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInNew());
|
||||
|
||||
*out_file = std::move(file);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *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<RemoteDirectory>(d);
|
||||
FsDir d;
|
||||
R_TRY(fsFsOpenDirectory(std::addressof(this->base_fs), sf_path.str, mode, &d));
|
||||
|
||||
auto dir = std::make_unique<RemoteDirectory>(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<FsFileSystemQueryId>(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<FsFileSystemQueryId>(query));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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()); }
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
union RightsId {
|
||||
u8 data[0x10];
|
||||
u64 data64[2];
|
||||
};
|
||||
static_assert(sizeof(RightsId) == 0x10);
|
||||
static_assert(std::is_pod<RightsId>::value);
|
||||
|
||||
/* Rights ID API */
|
||||
Result GetRightsId(RightsId *out, const char *path);
|
||||
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<IStorage> unique_storage;
|
||||
std::unique_ptr<IStorage> dir_bucket_storage;
|
||||
std::unique_ptr<IStorage> dir_entry_storage;
|
||||
std::unique_ptr<IStorage> file_bucket_storage;
|
||||
std::unique_ptr<IStorage> 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<IStorage>&& 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<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include "fs_save_data_types.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result CommitSaveData(const char *path);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<UserId>::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<SaveDataCreationInfo>::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<SaveDataAttribute>::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<SaveDataExtraData>::value);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result MountSdCard(const char *name);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
bool IsSignedSystemPartitionOnSdCardValid(const char *system_root_path);
|
||||
bool IsSignedSystemPartitionOnSdCardValidDeprecated();
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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<IStorage> 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<IStorage> 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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
||||
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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<IDirectory> *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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<IFileSystem> &&fs);
|
||||
Result Register(const char *name, std::unique_ptr<IFileSystem> &&fs, std::unique_ptr<ICommonMountNameGenerator> &&generator);
|
||||
Result Register(const char *name, std::unique_ptr<IFileSystem> &&fs, std::unique_ptr<ICommonMountNameGenerator> &&generator, bool use_data_cache, bool use_path_cache, bool multi_commit_supported);
|
||||
|
||||
void Unregister(const char *name);
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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";
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_memory_management.hpp>
|
||||
|
||||
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, Newable *placement) {
|
||||
return placement;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -14,9 +14,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../fs/fs_common.hpp"
|
||||
#include "../fs/fs_directory.hpp"
|
||||
#include "../sf/sf_buffer_tags.hpp"
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/fs_directory.hpp>
|
||||
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||
|
||||
namespace ams::fssrv::sf {
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "fssystem/fssystem_utility.hpp"
|
||||
#include "fssystem/fssystem_external_code.hpp"
|
||||
#include "fssystem/fssystem_path_tool.hpp"
|
||||
#include "fssystem/fssystem_subdirectory_filesystem.hpp"
|
||||
#include "fssystem/fssystem_directory_redirection_filesystem.hpp"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
fs::fsa::IFileSystem *GetExternalCodeFileSystem(ncm::ProgramId program_id);
|
||||
|
||||
Result CreateExternalCode(Handle *out, ncm::ProgramId program_id);
|
||||
void DestroyExternalCode(ncm::ProgramId program_id);
|
||||
|
||||
}
|
||||
@@ -142,7 +142,11 @@ namespace ams::fssystem {
|
||||
};
|
||||
|
||||
/* Other utility. */
|
||||
Result EnsureDirectoryExistsRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
||||
Result HasFile(bool *out, fs::fsa::IFileSystem *fs, const char *path);
|
||||
Result HasDirectory(bool *out, fs::fsa::IFileSystem *fs, const char *path);
|
||||
|
||||
Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
||||
Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
||||
|
||||
template<typename F>
|
||||
NX_INLINE Result RetryFinitelyForTargetLocked(F f) {
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "kvdb_auto_buffer.hpp"
|
||||
#include <stratosphere/kvdb/kvdb_auto_buffer.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
@@ -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<u8 *>(std::malloc(size));
|
||||
if (this->buffer == nullptr) {
|
||||
return ResultAllocationFailed();
|
||||
}
|
||||
R_UNLESS(this->buffer != nullptr, ResultAllocationFailed());
|
||||
|
||||
this->size = size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
@@ -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))) {
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <sys/stat.h>
|
||||
#include "kvdb_bounded_string.hpp"
|
||||
#include "kvdb_file_key_value_store.hpp"
|
||||
#include <stratosphere/fs/fs_filesystem.hpp>
|
||||
#include <stratosphere/fs/fs_file.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_bounded_string.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_file_key_value_store.hpp>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../os.hpp"
|
||||
#include "kvdb_bounded_string.hpp"
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/fs/fs_directory.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_bounded_string.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
@@ -23,12 +24,11 @@ namespace ams::kvdb {
|
||||
NON_COPYABLE(FileKeyValueStore);
|
||||
NON_MOVEABLE(FileKeyValueStore);
|
||||
public:
|
||||
static constexpr size_t MaxPathLength = 0x300; /* TODO: FS_MAX_PATH - 1? */
|
||||
static constexpr size_t MaxFileLength = 0xFF;
|
||||
static constexpr char FileExtension[5] = ".val";
|
||||
static constexpr size_t FileExtensionLength = sizeof(FileExtension) - 1;
|
||||
static constexpr size_t MaxKeySize = (MaxFileLength - FileExtensionLength) / 2;
|
||||
using Path = kvdb::BoundedString<MaxPathLength>;
|
||||
using Path = kvdb::BoundedString<fs::EntryNameLengthMax>;
|
||||
using FileName = kvdb::BoundedString<MaxFileLength>;
|
||||
private:
|
||||
/* Subtypes. */
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <sys/stat.h>
|
||||
#include "kvdb_auto_buffer.hpp"
|
||||
#include "kvdb_archive.hpp"
|
||||
#include "kvdb_bounded_string.hpp"
|
||||
#include <stratosphere/fs/fs_file.hpp>
|
||||
#include <stratosphere/fs/fs_directory.hpp>
|
||||
#include <stratosphere/fs/fs_filesystem.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_auto_buffer.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_archive.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_bounded_string.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
@@ -251,8 +253,7 @@ namespace ams::kvdb {
|
||||
}
|
||||
};
|
||||
private:
|
||||
static constexpr size_t MaxPathLen = 0x300; /* TODO: FS_MAX_PATH - 1? */
|
||||
using Path = kvdb::BoundedString<MaxPathLen>;
|
||||
using Path = kvdb::BoundedString<fs::EntryNameLengthMax>;
|
||||
private:
|
||||
Index index;
|
||||
Path path;
|
||||
@@ -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<size_t>(archive_size)));
|
||||
R_TRY(fs::ReadFile(file, 0, dst->Get(), dst->GetSize()));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "ldr_types.hpp"
|
||||
#include <stratosphere/ldr/ldr_types.hpp>
|
||||
|
||||
namespace ams::ldr::pm {
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "../ncm/ncm_types.hpp"
|
||||
#include "../sf/sf_buffer_tags.hpp"
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
#include <stratosphere/ncm/ncm_program_location.hpp>
|
||||
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||
|
||||
namespace ams::ldr {
|
||||
|
||||
|
||||
21
libraries/libstratosphere/include/stratosphere/lr.hpp
Normal file
21
libraries/libstratosphere/include/stratosphere/lr.hpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
#include <stratosphere/lr/lr_api.hpp>
|
||||
#include <stratosphere/lr/lr_location_resolver_manager_impl.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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
#include <stratosphere/lr/lr_i_add_on_content_location_resolver.hpp>
|
||||
|
||||
namespace ams::lr {
|
||||
|
||||
class AddOnContentLocationResolver {
|
||||
NON_COPYABLE(AddOnContentLocationResolver);
|
||||
private:
|
||||
std::shared_ptr<IAddOnContentLocationResolver> interface;
|
||||
public:
|
||||
AddOnContentLocationResolver() { /* ... */ }
|
||||
explicit AddOnContentLocationResolver(std::shared_ptr<IAddOnContentLocationResolver> 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<ncm::ApplicationId>(ids, num_ids));
|
||||
}
|
||||
|
||||
Result UnregisterApplicationAddOnContent(ncm::ApplicationId id) {
|
||||
AMS_ASSERT(this->interface);
|
||||
return this->interface->UnregisterApplicationAddOnContent(id);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
35
libraries/libstratosphere/include/stratosphere/lr/lr_api.hpp
Normal file
35
libraries/libstratosphere/include/stratosphere/lr/lr_api.hpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
#include <stratosphere/lr/lr_location_resolver.hpp>
|
||||
#include <stratosphere/lr/lr_add_on_content_location_resolver.hpp>
|
||||
#include <stratosphere/lr/lr_registered_location_resolver.hpp>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
|
||||
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<Path> 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<ncm::ApplicationId> &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),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
|
||||
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<Path> out, ncm::ProgramId id) = 0;
|
||||
virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) = 0;
|
||||
virtual Result ResolveApplicationControlPath(sf::Out<Path> out, ncm::ProgramId id) = 0;
|
||||
virtual Result ResolveApplicationHtmlDocumentPath(sf::Out<Path> out, ncm::ProgramId id) = 0;
|
||||
virtual Result ResolveDataPath(sf::Out<Path> 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<Path> 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<ncm::ProgramId> &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<Path> 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),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
#include <stratosphere/lr/lr_i_location_resolver.hpp>
|
||||
#include <stratosphere/lr/lr_i_add_on_content_location_resolver.hpp>
|
||||
#include <stratosphere/lr/lr_i_registered_location_resolver.hpp>
|
||||
|
||||
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<std::shared_ptr<ILocationResolver>> out, ncm::StorageId storage_id) = 0;
|
||||
virtual Result OpenRegisteredLocationResolver(sf::Out<std::shared_ptr<IRegisteredLocationResolver>> out) = 0;
|
||||
virtual Result RefreshLocationResolver(ncm::StorageId storage_id) = 0;
|
||||
virtual Result OpenAddOnContentLocationResolver(sf::Out<std::shared_ptr<IAddOnContentLocationResolver>> out) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
|
||||
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<Path> 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<Path> 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<ncm::ProgramId> &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),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
#include <stratosphere/lr/lr_i_location_resolver.hpp>
|
||||
|
||||
namespace ams::lr {
|
||||
|
||||
class LocationResolver {
|
||||
NON_COPYABLE(LocationResolver);
|
||||
private:
|
||||
std::shared_ptr<ILocationResolver> interface;
|
||||
public:
|
||||
LocationResolver() { /* ... */ }
|
||||
explicit LocationResolver(std::shared_ptr<ILocationResolver> 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<ncm::ProgramId>(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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
#include <stratosphere/lr/lr_i_location_resolver_manager.hpp>
|
||||
#include <stratosphere/ncm/ncm_bounded_map.hpp>
|
||||
|
||||
namespace ams::lr {
|
||||
|
||||
class LocationResolverManagerImpl final : public ILocationResolverManager {
|
||||
private:
|
||||
/* Resolver storage. */
|
||||
ncm::BoundedMap<ncm::StorageId, std::shared_ptr<ILocationResolver>, 5> location_resolvers;
|
||||
std::shared_ptr<IRegisteredLocationResolver> registered_location_resolver = nullptr;
|
||||
std::shared_ptr<IAddOnContentLocationResolver> add_on_content_location_resolver = nullptr;
|
||||
|
||||
os::Mutex mutex;
|
||||
public:
|
||||
/* Actual commands. */
|
||||
virtual Result OpenLocationResolver(sf::Out<std::shared_ptr<ILocationResolver>> out, ncm::StorageId storage_id) override;
|
||||
virtual Result OpenRegisteredLocationResolver(sf::Out<std::shared_ptr<IRegisteredLocationResolver>> out) override;
|
||||
virtual Result RefreshLocationResolver(ncm::StorageId storage_id) override;
|
||||
virtual Result OpenAddOnContentLocationResolver(sf::Out<std::shared_ptr<IAddOnContentLocationResolver>> 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),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/lr/lr_types.hpp>
|
||||
#include <stratosphere/lr/lr_i_registered_location_resolver.hpp>
|
||||
|
||||
namespace ams::lr {
|
||||
|
||||
class RegisteredLocationResolver {
|
||||
NON_COPYABLE(RegisteredLocationResolver);
|
||||
private:
|
||||
std::shared_ptr<IRegisteredLocationResolver> interface;
|
||||
public:
|
||||
RegisteredLocationResolver() { /* ... */ }
|
||||
explicit RegisteredLocationResolver(std::shared_ptr<IRegisteredLocationResolver> 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<ncm::ProgramId>(excluding_ids, num_ids));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fs/fs_directory.hpp>
|
||||
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||
|
||||
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<Path>::value && sizeof(Path) == fs::EntryNameLengthMax);
|
||||
|
||||
}
|
||||
@@ -16,4 +16,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ncm/ncm_types.hpp"
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
#include <stratosphere/ncm/ncm_program_location.hpp>
|
||||
#include <stratosphere/ncm/ncm_auto_buffer.hpp>
|
||||
#include <stratosphere/ncm/ncm_make_path.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_id_utils.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_manager_impl.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
||||
#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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_i_content_manager.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
/* Management. */
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
void InitializeWithObject(std::shared_ptr<IContentManager> 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);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
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<u8 *>(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();
|
||||
}
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user