Compare commits

..

24 Commits

Author SHA1 Message Date
Dave Murphy
22756068dd
npdmtool: don't error with missing debug flags, error with multiple set (#50) 2024-10-23 16:24:50 +01:00
Dave Murphy
0098896307
bump version 2024-10-14 21:15:30 +01:00
Michael Scire
28bd095683 elf2nro: add support for aligned header flag 2024-10-14 20:09:52 +01:00
Michael Scire
e36a8251cd npdmtool: add new flags from 18.0.0/19.0.0 2024-10-14 20:09:52 +01:00
Michael Scire
786ccde466 ndpmtool/elftool: support force_debug_prod 2024-10-14 20:09:52 +01:00
Dave Murphy
22471a1f5a
next release is 1.12.0 2022-05-22 16:16:35 +01:00
Michael Scire
becb4df6f6 use actual lowest/highest priorities, ignoring field names 2022-05-22 16:11:49 +01:00
Michael Scire
70d270d001 Add support for optional OptimizeMemoryAllocation npdm flag 2022-05-22 16:11:49 +01:00
Michael Scire
6fd5ae6e48 generate correct save_data_owner_ids 2022-05-22 16:11:49 +01:00
Michael Scire
87f4744e62 elf2kip/npdmtool: update json format to reflect modern OS.
Version field was incorrectly labeled "process_category". This is now supported (and defaults to 0 if not present on npdm, 1 if not present on kip). process_category is alias.

Support was added for mesosphere large-address map extension (these bits are reserved in official OS).

Support was added for specifying the signature key generation, which determines modulus used to verify ACID.

Support was added for system call capabilities in range [0x80, 0xBF], which kernel allows since 11.0.0.

"title_id" (and min/max) were renamed to program_id. If program_id not present, title_id used as alias.
2022-05-22 16:11:49 +01:00
Dave Murphy
6bad2b90e6 remove type redefinitions 2021-12-27 02:45:15 +00:00
Dave Murphy
3b5794ef95 next release is 1.11.0 2021-11-01 15:26:30 +00:00
Michael Scire
6f1d635208 build_romfs: fix use-after-free in romfs building 2021-07-17 16:02:12 +02:00
Michael Scire
f6561ef910 romfs: trim trailing spaces 2021-07-17 16:02:12 +02:00
Dave Murphy
660305f32e next release is 1.10.0 2021-04-12 16:24:56 +01:00
Michael Scire
07835aa357 elf2kip: add support for 12.0.0 immortal process flag 2021-04-10 17:22:07 +02:00
Dave Murphy
f85a558fc4 bump version for next release 2020-12-15 20:26:14 +00:00
fincs
c2977a7167 nxlink: fix Windows build 2020-12-02 13:16:31 +01:00
Michael Theall
1b660e22c3 Use poll to avoid busy-looping 2020-12-02 12:48:58 +01:00
Michael Scire
72b9e5cc85 npdmtool: be friendlier about disable das field 2020-12-02 02:55:22 +01:00
Michael Scire
fe13e1c1da npdmtool: add support for 11.0.0 DisableDeviceAddressSpaceMerge 2020-12-02 02:00:05 +01:00
Michael Scire
980dbbfe4c npdmtool: add compatibility for 8.0.0+ memory region capabilities 2020-08-21 20:22:07 +02:00
Dave Murphy
793b056f5b next release is 1.8.0 2020-08-06 15:47:50 +01:00
SciresM
48ec411d85
elf2kip: add compatibility for 8.0.0+ memory region capabilities (#30) 2020-08-06 15:32:09 +01:00
6 changed files with 551 additions and 383 deletions

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61) AC_PREREQ(2.61)
AC_INIT([switch-tools],[1.7.1],[https://github.com/switchbrew/switch-tools/issues]) AC_INIT([switch-tools],[1.13.1],[https://github.com/switchbrew/switch-tools/issues])
AC_CONFIG_SRCDIR([src/build_pfs0.c]) AC_CONFIG_SRCDIR([src/build_pfs0.c])
AM_INIT_AUTOMAKE([subdir-objects]) AM_INIT_AUTOMAKE([subdir-objects])
@ -31,7 +31,7 @@ NET_LIBS=""
case "$host" in case "$host" in
*-*-mingw*) *-*-mingw*)
NET_LIBS="-lws2_32" NET_LIBS="-lws2_32"
CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO" CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO -D_WIN32_WINNT=0x0600"
;; ;;
esac esac

View File

@ -9,11 +9,6 @@
#include "blz.h" #include "blz.h"
#include "elf64.h" #include "elf64.h"
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef struct { typedef struct {
u32 DstOff; u32 DstOff;
u32 DecompSz; u32 DecompSz;
@ -24,8 +19,8 @@ typedef struct {
typedef struct { typedef struct {
u8 Magic[4]; u8 Magic[4];
u8 Name[0xC]; u8 Name[0xC];
u64 TitleId; u64 ProgramId;
u32 ProcessCategory; u32 Version;
u8 MainThreadPriority; u8 MainThreadPriority;
u8 DefaultCpuId; u8 DefaultCpuId;
u8 Unk; u8 Unk;
@ -190,6 +185,32 @@ int cJSON_GetU64FromObjectValue(const cJSON *config, u64 *out) {
} }
} }
int cJSON_GetU32(const cJSON *obj, const char *field, u32 *out) {
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
if (cJSON_IsString(config) && (config->valuestring != NULL)) {
char *endptr = NULL;
*out = strtoul(config->valuestring, &endptr, 16);
if (config->valuestring == endptr) {
fprintf(stderr, "Failed to get %s (empty string)\n", field);
return 0;
} else if (errno == ERANGE) {
fprintf(stderr, "Failed to get %s (value out of range)\n", field);
return 0;
} else if (errno == EINVAL) {
fprintf(stderr, "Failed to get %s (not base16 string)\n", field);
return 0;
} else if (errno) {
fprintf(stderr, "Failed to get %s (unknown error)\n", field);
return 0;
} else {
return 1;
}
} else {
fprintf(stderr, "Failed to get %s (field not present).\n", field);
return 0;
}
}
int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
const cJSON *capability = NULL; const cJSON *capability = NULL;
const cJSON *capabilities = NULL; const cJSON *capabilities = NULL;
@ -214,8 +235,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
/* Parse title_id. */ /* Parse program_id (or deprecated title_id). */
if (!cJSON_GetU64(npdm_json, "title_id", &kip_hdr->TitleId)) { if (!cJSON_GetU64(npdm_json, "program_id", &kip_hdr->ProgramId) && !cJSON_GetU64(npdm_json, "title_id", &kip_hdr->ProgramId)) {
status = 0; status = 0;
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
@ -231,6 +252,17 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
} }
} }
/* Parse immortality. */
/* This field is optional, and defaults to true (set before this function is called). */
int immortal = 1;
if (cJSON_GetBooleanOptional(npdm_json, "immortal", &immortal)) {
if (immortal) {
kip_hdr->Flags |= 0x40;
} else {
kip_hdr->Flags &= ~0x40;
}
}
/* Parse main_thread_stack_size. */ /* Parse main_thread_stack_size. */
u64 stack_size = 0; u64 stack_size = 0;
if (!cJSON_GetU64(npdm_json, "main_thread_stack_size", &stack_size)) { if (!cJSON_GetU64(npdm_json, "main_thread_stack_size", &stack_size)) {
@ -253,9 +285,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
status = 0; status = 0;
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
if (!cJSON_GetU8(npdm_json, "process_category", (u8 *)&kip_hdr->ProcessCategory)) { if (!cJSON_GetU32(npdm_json, "version", &kip_hdr->Version) && !cJSON_GetU32(npdm_json, "process_category", &kip_hdr->Version)) { // optional
status = 0; kip_hdr->Version = 1;
goto PARSE_CAPS_END;
} }
/* Parse capabilities. */ /* Parse capabilities. */
@ -310,13 +341,17 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
status = 0; status = 0;
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
u8 real_highest_prio = (lowest_prio < highest_prio) ? lowest_prio : highest_prio;
u8 real_lowest_prio = (lowest_prio > highest_prio) ? lowest_prio : highest_prio;
desc = highest_cpu; desc = highest_cpu;
desc <<= 8; desc <<= 8;
desc |= lowest_cpu; desc |= lowest_cpu;
desc <<= 6; desc <<= 6;
desc |= (lowest_prio & 0x3F); desc |= (real_highest_prio & 0x3F);
desc <<= 6; desc <<= 6;
desc |= (highest_prio & 0x3F); desc |= (real_lowest_prio & 0x3F);
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 4) | (0x0007)); kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 4) | (0x0007));
} else if (!strcmp(type_str, "syscalls")) { } else if (!strcmp(type_str, "syscalls")) {
if (!cJSON_IsObject(value)) { if (!cJSON_IsObject(value)) {
@ -325,7 +360,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
u32 num_descriptors; u32 num_descriptors;
u32 descriptors[6] = {0}; /* alignup(0x80/0x18); */ u32 descriptors[8] = {0}; /* alignup(0xC0/0x18); */
char field_name[8] = {0}; char field_name[8] = {0};
const cJSON *cur_syscall = NULL; const cJSON *cur_syscall = NULL;
u64 syscall_value = 0; u64 syscall_value = 0;
@ -338,14 +373,14 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
if (syscall_value >= 0x80) { if (syscall_value >= 0xC0) {
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0x7F]\n"); fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0xBF]\n");
status = 0; status = 0;
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18)); descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18));
} }
for (unsigned int i = 0; i < 6; i++) { for (unsigned int i = 0; i < 8; i++) {
if (descriptors[i]) { if (descriptors[i]) {
if (cur_cap + 1 > 0x20) { if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n"); fprintf(stderr, "Error: Too many capabilities!\n");
@ -382,7 +417,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
desc |= is_ro << 24; desc |= is_ro << 24;
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F)); kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F));
desc = (u32)((map_size >> 12) & 0x00FFFFFFULL); desc = (u32)((map_size >> 12) & 0x000FFFFFULL);
desc |= (u32)(((map_address >> 36) & 0xFULL) << 20);
is_io ^= 1; is_io ^= 1;
desc |= is_io << 24; desc |= is_io << 24;
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F)); kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F));
@ -399,6 +435,47 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
} }
desc = (u32)((page_address >> 12) & 0x00FFFFFFULL); desc = (u32)((page_address >> 12) & 0x00FFFFFFULL);
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 8) | (0x007F)); kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 8) | (0x007F));
} else if (!strcmp(type_str, "map_region")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_IsArray(value)) {
fprintf(stderr, "Map Region capability value must be array!\n");
status = 0;
goto PARSE_CAPS_END;
}
u8 regions[3] = {0};
int is_ro[3] = {0};
const cJSON *cur_region = NULL;
int index = 0;
cJSON_ArrayForEach(cur_region, value) {
if (index >= 3) {
fprintf(stderr, "Too many region descriptors!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_IsObject(cur_region)) {
fprintf(stderr, "Region descriptor value must be object!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetU8(cur_region, "region_type", &regions[index]) ||
!cJSON_GetBoolean(cur_region, "is_ro", &is_ro[index])) {
status = 0;
goto PARSE_CAPS_END;
}
index++;
}
u32 capability = 0x3FF;
for (int i = 0; i < 3; ++i) {
capability |= ((regions[i] & 0x3F) | ((is_ro[i] & 1) << 6)) << (11 + 7 * i);
}
kip_hdr->Capabilities[cur_cap++] = capability;
} else if (!strcmp(type_str, "irq_pair")) { } else if (!strcmp(type_str, "irq_pair")) {
if (cur_cap + 1 > 0x20) { if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n"); fprintf(stderr, "Error: Too many capabilities!\n");
@ -477,6 +554,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
} }
int allow_debug = 0; int allow_debug = 0;
int force_debug = 0; int force_debug = 0;
int force_debug_prod = 0;
if (!cJSON_GetBoolean(value, "allow_debug", &allow_debug)) { if (!cJSON_GetBoolean(value, "allow_debug", &allow_debug)) {
status = 0; status = 0;
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
@ -485,7 +563,11 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
status = 0; status = 0;
goto PARSE_CAPS_END; goto PARSE_CAPS_END;
} }
desc = (allow_debug & 1) | ((force_debug & 1) << 1); if (!cJSON_GetBoolean(value, "force_debug_prod", &force_debug_prod)) {
status = 0;
goto PARSE_CAPS_END;
}
desc = (allow_debug & 1) | ((force_debug_prod & 1) << 1) | ((force_debug & 1) << 2);
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 17) | (0xFFFF)); kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 17) | (0xFFFF));
} else { } else {
fprintf(stderr, "Error: unknown capability %s\n", type_str); fprintf(stderr, "Error: unknown capability %s\n", type_str);
@ -512,7 +594,7 @@ int main(int argc, char* argv[]) {
KipHeader kip_hdr = {0}; KipHeader kip_hdr = {0};
memcpy(kip_hdr.Magic, "KIP1", 4); memcpy(kip_hdr.Magic, "KIP1", 4);
kip_hdr.Flags = 0x3F; kip_hdr.Flags = 0x7F;
if (sizeof(KipHeader) != 0x100) { if (sizeof(KipHeader) != 0x100) {
fprintf(stderr, "Bad compile environment!\n"); fprintf(stderr, "Bad compile environment!\n");

View File

@ -8,10 +8,6 @@
#include "elf64.h" #include "elf64.h"
#include "romfs.h" #include "romfs.h"
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint8_t u8;
typedef struct { typedef struct {
u32 FileOff; u32 FileOff;
u32 Size; u32 Size;
@ -25,9 +21,9 @@ typedef struct {
typedef struct { typedef struct {
u8 Magic[4]; u8 Magic[4];
u32 Unk1; u32 version;
u32 size; u32 size;
u32 Unk2; u32 flags;
NsoSegment Segments[3]; NsoSegment Segments[3];
u32 bssSize; u32 bssSize;
u32 Unk3; u32 Unk3;
@ -82,6 +78,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "--nacp=<control.nacp> Embeds control.nacp into the output file.\n"); fprintf(stderr, "--nacp=<control.nacp> Embeds control.nacp into the output file.\n");
fprintf(stderr, "--romfs=<image> Embeds RomFS into the output file.\n"); fprintf(stderr, "--romfs=<image> Embeds RomFS into the output file.\n");
fprintf(stderr, "--romfsdir=<directory> Builds and embeds RomFS into the output file.\n"); fprintf(stderr, "--romfsdir=<directory> Builds and embeds RomFS into the output file.\n");
fprintf(stderr, "--alignedheader Sets the \"AlignedHeader\" flag in the output file.\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -106,11 +103,13 @@ int main(int argc, char* argv[]) {
int argi; int argi;
char* icon_path = NULL, *nacp_path = NULL, *romfs_path = NULL, *romfs_dir_path = NULL; char* icon_path = NULL, *nacp_path = NULL, *romfs_path = NULL, *romfs_dir_path = NULL;
u32 aligned_header = 0;
for (argi=3; argi<argc; argi++) { for (argi=3; argi<argc; argi++) {
if (strncmp(argv[argi], "--icon=", 7)==0) icon_path = &argv[argi][7]; if (strncmp(argv[argi], "--icon=", 7)==0) icon_path = &argv[argi][7];
if (strncmp(argv[argi], "--nacp=", 7)==0) nacp_path = &argv[argi][7]; if (strncmp(argv[argi], "--nacp=", 7)==0) nacp_path = &argv[argi][7];
if (strncmp(argv[argi], "--romfs=", 8)==0) romfs_path = &argv[argi][8]; if (strncmp(argv[argi], "--romfs=", 8)==0) romfs_path = &argv[argi][8];
if (strncmp(argv[argi], "--romfsdir=", 11)==0) romfs_dir_path = &argv[argi][11]; if (strncmp(argv[argi], "--romfsdir=", 11)==0) romfs_dir_path = &argv[argi][11];
if (strncmp(argv[argi], "--alignedheader", 15)==0) aligned_header = 1;
} }
if (romfs_dir_path != NULL && romfs_path != NULL) { if (romfs_dir_path != NULL && romfs_path != NULL) {
@ -213,6 +212,9 @@ int main(int argc, char* argv[]) {
nro_hdr.size = file_off; nro_hdr.size = file_off;
nro_hdr.version = 0;
nro_hdr.flags = (aligned_header << 0);
// TODO check retvals // TODO check retvals
for (i=0; i<3; i++) for (i=0; i<3; i++)

View File

@ -45,7 +45,7 @@ typedef struct {
typedef struct { typedef struct {
u32 Magic; u32 Magic;
u8 _0x4[0xC]; u8 _0x4[0xC];
u64 TitleId; u64 ProgramId;
u64 _0x18; u64 _0x18;
u32 FahOffset; u32 FahOffset;
u32 FahSize; u32 FahSize;
@ -63,8 +63,8 @@ typedef struct {
u32 Size; u32 Size;
u32 _0x208; u32 _0x208;
u32 Flags; u32 Flags;
u64 TitleIdRangeMin; u64 ProgramIdRangeMin;
u64 TitleIdRangeMax; u64 ProgramIdRangeMax;
u32 FacOffset; u32 FacOffset;
u32 FacSize; u32 FacSize;
u32 SacOffset; u32 SacOffset;
@ -76,7 +76,7 @@ typedef struct {
typedef struct { typedef struct {
u32 Magic; u32 Magic;
u32 _0x4; u32 SignatureKeyGeneration;
u32 _0x8; u32 _0x8;
u8 MmuFlags; u8 MmuFlags;
u8 _0xD; u8 _0xD;
@ -84,7 +84,7 @@ typedef struct {
u8 DefaultCpuId; u8 DefaultCpuId;
u32 _0x10; u32 _0x10;
u32 SystemResourceSize; u32 SystemResourceSize;
u32 ProcessCategory; u32 Version;
u32 MainThreadStackSize; u32 MainThreadStackSize;
char Name[0x10]; char Name[0x10];
char ProductCode[0x10]; char ProductCode[0x10];
@ -345,10 +345,11 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
cJSON_GetU32(npdm_json, "system_resource_size", &header.SystemResourceSize); // optional cJSON_GetU32(npdm_json, "system_resource_size", &header.SystemResourceSize); // optional
if (!cJSON_GetU8(npdm_json, "process_category", (u8 *)&header.ProcessCategory)) { /* Get version (deprecated name "process_category"). */
status = 0; if (!cJSON_GetU32(npdm_json, "version", &header.Version) && !cJSON_GetU32(npdm_json, "process_category", &header.Version)) { // optional
goto NPDM_BUILD_END; header.Version = 0;
} }
if (!cJSON_GetU8(npdm_json, "address_space_type", (u8 *)&header.MmuFlags)) { if (!cJSON_GetU8(npdm_json, "address_space_type", (u8 *)&header.MmuFlags)) {
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
@ -362,6 +363,33 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
} }
header.MmuFlags |= is_64_bit; header.MmuFlags |= is_64_bit;
int optimize_memory_allocation; // optional
if (cJSON_GetBoolean(npdm_json, "optimize_memory_allocation", &optimize_memory_allocation)) {
header.MmuFlags |= ((optimize_memory_allocation & 1) << 4);
}
int disable_device_address_space_merge; // optional
if (cJSON_GetBoolean(npdm_json, "disable_device_address_space_merge", &disable_device_address_space_merge)) {
header.MmuFlags |= ((disable_device_address_space_merge & 1) << 5);
}
int enable_alias_region_extra_size; // optional
if (cJSON_GetBoolean(npdm_json, "enable_alias_region_extra_size", &enable_alias_region_extra_size)) {
header.MmuFlags |= ((enable_alias_region_extra_size & 1) << 6);
}
int prevent_code_reads; // optional
if (cJSON_GetBoolean(npdm_json, "prevent_code_reads", &prevent_code_reads)) {
header.MmuFlags |= ((prevent_code_reads & 1) << 7);
}
u8 signature_key_generation; // optional
if (cJSON_GetU8(npdm_json, "signature_key_generation", &signature_key_generation)) {
header.SignatureKeyGeneration = signature_key_generation;
} else {
header.SignatureKeyGeneration = 0;
}
/* ACID. */ /* ACID. */
memset(acid->Signature, 0, sizeof(acid->Signature)); memset(acid->Signature, 0, sizeof(acid->Signature));
memset(acid->Modulus, 0, sizeof(acid->Modulus)); memset(acid->Modulus, 0, sizeof(acid->Modulus));
@ -379,19 +407,19 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
} }
acid->Flags |= (pool_partition & 3) << 2; acid->Flags |= (pool_partition & 3) << 2;
if (!cJSON_GetU64(npdm_json, "title_id_range_min", &acid->TitleIdRangeMin)) { if (!cJSON_GetU64(npdm_json, "program_id_range_min", &acid->ProgramIdRangeMin) && !cJSON_GetU64(npdm_json, "title_id_range_min", &acid->ProgramIdRangeMin)) {
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
if (!cJSON_GetU64(npdm_json, "title_id_range_max", &acid->TitleIdRangeMax)) { if (!cJSON_GetU64(npdm_json, "program_id_range_max", &acid->ProgramIdRangeMax) && !cJSON_GetU64(npdm_json, "title_id_range_max", &acid->ProgramIdRangeMax)) {
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
/* ACI0. */ /* ACI0. */
aci0->Magic = MAGIC_ACI0; /* "ACI0" */ aci0->Magic = MAGIC_ACI0; /* "ACI0" */
/* Parse title_id. */ /* Parse program_id (or deprecated title_id). */
if (!cJSON_GetU64(npdm_json, "title_id", &aci0->TitleId)) { if (!cJSON_GetU64(npdm_json, "program_id", &aci0->ProgramId) && !cJSON_GetU64(npdm_json, "title_id", &aci0->ProgramId)) {
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
@ -453,18 +481,33 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
sdois = cJSON_GetObjectItemCaseSensitive(fsaccess, "save_data_owner_ids"); sdois = cJSON_GetObjectItemCaseSensitive(fsaccess, "save_data_owner_ids");
if (cJSON_IsArray(sdois)) { if (cJSON_IsArray(sdois)) {
u32 *count = (u32 *)((u8 *)fah + fah->SdoiOffset); u32 *count = (u32 *)((u8 *)fah + fah->SdoiOffset);
u64 *id = (u64 *)((u8 *)count + sizeof(u32));
cJSON_ArrayForEach(sdoi, sdois) { cJSON_ArrayForEach(sdoi, sdois) {
if (!cJSON_GetU64FromObjectValue(sdoi, id)) { if (!cJSON_IsObject(sdoi)) {
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
++id;
++(*count); ++(*count);
} }
u8 *accessibility = (u8 *)count + sizeof(u32);
u64 *id = (u64 *)(accessibility + (((*count) + 3ULL) & ~3ULL));
cJSON_ArrayForEach(sdoi, sdois) {
if (!cJSON_GetU8(sdoi, "accessibility", accessibility)) {
status = 0;
goto NPDM_BUILD_END;
}
if (!cJSON_GetU64(sdoi, "id", id)) {
status = 0;
goto NPDM_BUILD_END;
}
++accessibility;
++id;
}
if (*count > 0) { if (*count > 0) {
fah->SdoiSize = sizeof(u32) + sizeof(u64) * (*count); fah->SdoiSize = sizeof(u32) + sizeof(u8) * ((((*count) + 3ULL) & ~3ULL)) + sizeof(u64) * (*count);
} }
} }
@ -613,13 +656,17 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
u8 real_highest_prio = (lowest_prio < highest_prio) ? lowest_prio : highest_prio;
u8 real_lowest_prio = (lowest_prio > highest_prio) ? lowest_prio : highest_prio;
desc = highest_cpu; desc = highest_cpu;
desc <<= 8; desc <<= 8;
desc |= lowest_cpu; desc |= lowest_cpu;
desc <<= 6; desc <<= 6;
desc |= (lowest_prio & 0x3F); desc |= (real_highest_prio & 0x3F);
desc <<= 6; desc <<= 6;
desc |= (highest_prio & 0x3F); desc |= (real_lowest_prio & 0x3F);
caps[cur_cap++] = (u32)((desc << 4) | (0x0007)); caps[cur_cap++] = (u32)((desc << 4) | (0x0007));
} else if (!strcmp(type_str, "syscalls")) { } else if (!strcmp(type_str, "syscalls")) {
if (!cJSON_IsObject(value)) { if (!cJSON_IsObject(value)) {
@ -628,7 +675,7 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
u32 num_descriptors; u32 num_descriptors;
u32 descriptors[6] = {0}; /* alignup(0x80/0x18); */ u32 descriptors[8] = {0}; /* alignup(0xC0/0x18); */
char field_name[8] = {0}; char field_name[8] = {0};
const cJSON *cur_syscall = NULL; const cJSON *cur_syscall = NULL;
u64 syscall_value = 0; u64 syscall_value = 0;
@ -641,14 +688,14 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
if (syscall_value >= 0x80) { if (syscall_value >= 0xC0) {
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0x7F]\n"); fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0xBF]\n");
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18)); descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18));
} }
for (unsigned int i = 0; i < 6; i++) { for (unsigned int i = 0; i < 8; i++) {
if (descriptors[i]) { if (descriptors[i]) {
desc = descriptors[i] | (i << 24); desc = descriptors[i] | (i << 24);
caps[cur_cap++] = (u32)((desc << 5) | (0x000F)); caps[cur_cap++] = (u32)((desc << 5) | (0x000F));
@ -675,7 +722,8 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
desc |= is_ro << 24; desc |= is_ro << 24;
caps[cur_cap++] = (u32)((desc << 7) | (0x003F)); caps[cur_cap++] = (u32)((desc << 7) | (0x003F));
desc = (u32)((map_size >> 12) & 0x00FFFFFFULL); desc = (u32)((map_size >> 12) & 0x000FFFFFULL);
desc |= (u32)(((map_address >> 36) & 0xFULL) << 20);
is_io ^= 1; is_io ^= 1;
desc |= is_io << 24; desc |= is_io << 24;
caps[cur_cap++] = (u32)((desc << 7) | (0x003F)); caps[cur_cap++] = (u32)((desc << 7) | (0x003F));
@ -687,6 +735,47 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
} }
desc = (u32)((page_address >> 12) & 0x00FFFFFFULL); desc = (u32)((page_address >> 12) & 0x00FFFFFFULL);
caps[cur_cap++] = (u32)((desc << 8) | (0x007F)); caps[cur_cap++] = (u32)((desc << 8) | (0x007F));
} else if (!strcmp(type_str, "map_region")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto NPDM_BUILD_END;
}
if (!cJSON_IsArray(value)) {
fprintf(stderr, "Map Region capability value must be array!\n");
status = 0;
goto NPDM_BUILD_END;
}
u8 regions[3] = {0};
int is_ro[3] = {0};
const cJSON *cur_region = NULL;
int index = 0;
cJSON_ArrayForEach(cur_region, value) {
if (index >= 3) {
fprintf(stderr, "Too many region descriptors!\n");
status = 0;
goto NPDM_BUILD_END;
}
if (!cJSON_IsObject(cur_region)) {
fprintf(stderr, "Region descriptor value must be object!\n");
status = 0;
goto NPDM_BUILD_END;
}
if (!cJSON_GetU8(cur_region, "region_type", &regions[index]) ||
!cJSON_GetBoolean(cur_region, "is_ro", &is_ro[index])) {
status = 0;
goto NPDM_BUILD_END;
}
index++;
}
u32 capability = 0x3FF;
for (int i = 0; i < 3; ++i) {
capability |= ((regions[i] & 0x3F) | ((is_ro[i] & 1) << 6)) << (11 + 7 * i);
}
caps[cur_cap++] = capability;
} else if (!strcmp(type_str, "irq_pair")) { } else if (!strcmp(type_str, "irq_pair")) {
if (!cJSON_IsArray(value) || cJSON_GetArraySize(value) != 2) { if (!cJSON_IsArray(value) || cJSON_GetArraySize(value) != 2) {
fprintf(stderr, "Error: IRQ Pairs must have size 2 array value.\n"); fprintf(stderr, "Error: IRQ Pairs must have size 2 array value.\n");
@ -740,15 +829,16 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
} }
int allow_debug = 0; int allow_debug = 0;
int force_debug = 0; int force_debug = 0;
if (!cJSON_GetBoolean(value, "allow_debug", &allow_debug)) { int force_debug_prod = 0;
cJSON_GetBoolean(value, "allow_debug", &allow_debug);
cJSON_GetBoolean(value, "force_debug", &force_debug);
cJSON_GetBoolean(value, "force_debug_prod", &force_debug_prod);
if ( allow_debug + force_debug + force_debug_prod > 1 ) {
fprintf(stderr, "Only one of allow_debug, force_debug, or force_debug_prod can be set!\n");
status = 0; status = 0;
goto NPDM_BUILD_END; goto NPDM_BUILD_END;
} }
if (!cJSON_GetBoolean(value, "force_debug", &force_debug)) { desc = (allow_debug & 1) | ((force_debug_prod & 1) << 1) | ((force_debug & 1) << 2);
status = 0;
goto NPDM_BUILD_END;
}
desc = (allow_debug & 1) | ((force_debug & 1) << 1);
caps[cur_cap++] = (u32)((desc << 17) | (0xFFFF)); caps[cur_cap++] = (u32)((desc << 17) | (0xFFFF));
} }
} }

View File

@ -14,11 +14,21 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
#include <poll.h>
#define closesocket close
#else #else
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
typedef int socklen_t; typedef int socklen_t;
typedef uint32_t in_addr_t; typedef uint32_t in_addr_t;
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
#ifdef EWOULDBLOCK
#undef EWOULDBLOCK
#endif
#define EWOULDBLOCK WSAEWOULDBLOCK
#define poll WSAPoll
#endif #endif
#include <zlib.h> #include <zlib.h>
@ -30,100 +40,95 @@ typedef uint32_t in_addr_t;
#define NETLOADER_CLIENT_PORT 28771 #define NETLOADER_CLIENT_PORT 28771
char cmdbuf[3072]; static char cmdbuf[3072];
uint32_t cmdlen=0; static uint32_t cmdlen=0;
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void shutdownSocket(int socket) { static void shutdownSocket(int socket, int flags) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
#ifdef __WIN32__ if (flags)
shutdown (socket, SD_SEND); shutdown(socket, flags);
closesocket (socket); closesocket(socket);
#else
close(socket);
#endif
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
static int set_socket_nonblocking(int sock) { static int setSocketNonblocking(int sock) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
#ifndef __WIN32__ #ifndef __WIN32__
int flags = fcntl(sock, F_GETFL); int flags = fcntl(sock, F_GETFL);
if(flags == -1) return -1; if (flags == -1) return -1;
int rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK); int rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
if(rc != 0) return -1; if (rc != 0) return -1;
#else #else
u_long opt = 1; u_long iMode = 1; // non-blocking
ioctlsocket(sock, FIONBIO, &opt);
int rc = ioctlsocket(sock, FIONBIO, &iMode);
if (rc != NO_ERROR) return -1;
#endif #endif
return 0; return 0;
} }
void socket_error(const char *msg) { //---------------------------------------------------------------------------------
static int socketError(const char *msg) {
//---------------------------------------------------------------------------------
#ifndef _WIN32 #ifndef _WIN32
int ret = errno;
if (ret == EAGAIN)
ret = EWOULDBLOCK;
perror(msg); perror(msg);
#else #else
int ret = WSAGetLastError();
wchar_t *s = NULL; wchar_t *s = NULL;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, WSAGetLastError(), NULL, ret,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&s, 0, NULL); (LPWSTR)&s, 0, NULL);
fprintf(stderr, "%S\n", s); fprintf(stderr, "%S\n", s);
LocalFree(s); LocalFree(s);
if (ret == WSAEWOULDBLOCK)
ret = EWOULDBLOCK;
#endif #endif
}
/*--------------------------------------------------------------------------------- return ret;
Subtract the `struct timeval' values Y from X,
storing the result in RESULT.
Return 1 if the difference is negative, otherwise 0.
From http://www.gnu.org/software/libtool/manual/libc/Elapsed-Time.html
---------------------------------------------------------------------------------*/
int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y) {
//---------------------------------------------------------------------------------
struct timeval tmp;
tmp.tv_sec = y->tv_sec;
tmp.tv_usec = y->tv_usec;
/* Perform the carry for the later subtraction by updating y. */
if (x->tv_usec < tmp.tv_usec) {
int nsec = (tmp.tv_usec - x->tv_usec) / 1000000 + 1;
tmp.tv_usec -= 1000000 * nsec;
tmp.tv_sec += nsec;
}
if (x->tv_usec - tmp.tv_usec > 1000000) {
int nsec = (x->tv_usec - tmp.tv_usec) / 1000000;
tmp.tv_usec += 1000000 * nsec;
tmp.tv_sec -= nsec;
}
/* Compute the time remaining to wait.
tv_usec is certainly positive. */
result->tv_sec = x->tv_sec - tmp.tv_sec;
result->tv_usec = x->tv_usec - tmp.tv_usec;
/* Return 1 if result is negative. */
return x->tv_sec < tmp.tv_sec;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void timeval_add (struct timeval *result, struct timeval *x, struct timeval *y) { int pollSocket(int fd, int events, int timeout) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
result->tv_sec = x->tv_sec + y->tv_sec; #ifndef __WIN32__
result->tv_usec = x->tv_usec + y->tv_usec; struct pollfd pfd;
#else
WSAPOLLFD pfd;
#endif
if ( result->tv_usec > 1000000) { pfd.fd = fd;
result->tv_sec += result->tv_usec / 1000000; pfd.events = events;
result->tv_usec = result->tv_usec % 1000000; pfd.revents = 0;
int ret = poll(&pfd, 1, timeout);
if (ret < 0) {
socketError("poll");
return -1;
} }
if (ret == 0)
return -1;
if (!(pfd.revents & events)) {
int err = 0;
int len = sizeof(err);
getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
fprintf(stderr, "socket error 0x%x on poll\n", err);
return -1;
}
return 0;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -137,7 +142,7 @@ static struct in_addr findSwitch(int retries) {
char mess[] = "nxboot"; char mess[] = "nxboot";
int broadcastSock = socket(PF_INET, SOCK_DGRAM, 0); int broadcastSock = socket(PF_INET, SOCK_DGRAM, 0);
if(broadcastSock < 0) socket_error("create send socket"); if (broadcastSock < 0) socketError("create send socket");
int optval = 1, len; int optval = 1, len;
setsockopt(broadcastSock, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)); setsockopt(broadcastSock, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval));
@ -154,102 +159,89 @@ static struct in_addr findSwitch(int retries) {
int recvSock = socket(PF_INET, SOCK_DGRAM, 0); int recvSock = socket(PF_INET, SOCK_DGRAM, 0);
if (recvSock < 0) socket_error("create receive socket"); if (recvSock < 0) socketError("create receive socket");
if(bind(recvSock, (struct sockaddr*) &rs, sizeof(rs)) < 0) socket_error("bind receive socket"); if (bind(recvSock, (struct sockaddr*) &rs, sizeof(rs)) < 0) socketError("bind receive socket");
set_socket_nonblocking(recvSock); setSocketNonblocking(recvSock);
struct timeval wanted, now, result; while (retries) {
if (sendto(broadcastSock, mess, strlen(mess), 0, (struct sockaddr *)&s, sizeof(s)) < 0)
socketError("sendto");
gettimeofday(&wanted, NULL); if (pollSocket(recvSock, POLLIN, 150) == 0) {
int timeout = retries;
while(timeout) {
gettimeofday(&now, NULL);
if ( timeval_subtract(&result,&wanted,&now)) {
if(sendto(broadcastSock, mess, strlen(mess), 0, (struct sockaddr *)&s, sizeof(s)) < 0) socket_error("sendto");
result.tv_sec=0;
result.tv_usec=150000;
timeval_add(&wanted,&now,&result);
timeout--;
}
socklen_t socklen = sizeof(remote); socklen_t socklen = sizeof(remote);
len = recvfrom(recvSock,recvbuf,sizeof(recvbuf),0,(struct sockaddr *)&remote,&socklen); len = recvfrom(recvSock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&remote, &socklen);
if ( len != -1) { if (len != -1) {
if ( strncmp("bootnx",recvbuf,strlen("bootnx")) == 0) { if (strncmp("bootnx", recvbuf, strlen("bootnx")) == 0) {
break; break;
} }
} }
} }
if (timeout == 0) remote.sin_addr.s_addr = INADDR_NONE;
shutdownSocket(broadcastSock); --retries;
shutdownSocket(recvSock); }
if (retries == 0)
remote.sin_addr.s_addr = INADDR_NONE;
shutdownSocket(broadcastSock, 0);
shutdownSocket(recvSock, SHUT_RD);
return remote.sin_addr; return remote.sin_addr;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int sendData(int sock, int sendsize, void *buffer) { static int sendData(int sock, int sendsize, void *buffer) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
char *buf = (char*)buffer; char *buf = (char*)buffer;
while(sendsize) { while (sendsize) {
if (pollSocket(sock, POLLOUT, -1))
return 1;
int len = send(sock, buf, sendsize, 0); int len = send(sock, buf, sendsize, 0);
if (len == 0) break; if (len == 0)
if (len != -1) { return 1;
if (len == -1) {
if (socketError("send") != EWOULDBLOCK)
return 1;
} else {
sendsize -= len; sendsize -= len;
buf += len; buf += len;
} else {
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
socket_error("sendData");
break;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
socket_error("sendData");
break;
}
#endif
} }
} }
return sendsize != 0; return sendsize != 0;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int recvData(int sock, void *buffer, int size, int flags) { static int recvData(int sock, void *buffer, int size, int flags) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int len, sizeleft = size; int len, sizeleft = size;
char *buf = (char*)buffer; char *buf = (char*)buffer;
while (sizeleft) { while (sizeleft) {
if (pollSocket(sock, POLLIN, -1))
return 0;
len = recv(sock,buf,sizeleft,flags); len = recv(sock,buf,sizeleft,flags);
if (len == 0) { if (len == 0)
size = 0; return 0;
break;
} if (len == -1) {
if (len != -1) { if (socketError("recv") != EWOULDBLOCK)
return 0;
} else {
sizeleft -=len; sizeleft -=len;
buf +=len; buf +=len;
} else {
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
printf("recvdata error %d\n",errcode);
break;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
socket_error("recvdata");
break;
}
#endif
} }
} }
return size; return size;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int sendInt32LE(int socket, uint32_t size) { static int sendInt32LE(int socket, uint32_t size) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
unsigned char lenbuf[4]; unsigned char lenbuf[4];
lenbuf[0] = size & 0xff; lenbuf[0] = size & 0xff;
@ -257,14 +249,14 @@ int sendInt32LE(int socket, uint32_t size) {
lenbuf[2] = (size >> 16) & 0xff; lenbuf[2] = (size >> 16) & 0xff;
lenbuf[3] = (size >> 24) & 0xff; lenbuf[3] = (size >> 24) & 0xff;
return sendData(socket,4,lenbuf); return sendData(socket, 4, lenbuf);
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int recvInt32LE(int socket, int32_t *data) { static int recvInt32LE(int socket, int32_t *data) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
unsigned char intbuf[4]; unsigned char intbuf[4];
int len = recvData(socket,intbuf,4,0); int len = recvData(socket, intbuf, 4, 0);
if (len == 4) { if (len == 4) {
*data = intbuf[0] & 0xff + (intbuf[1] << 8) + (intbuf[2] << 16) + (intbuf[3] << 24); *data = intbuf[0] & 0xff + (intbuf[1] << 8) + (intbuf[2] << 16) + (intbuf[3] << 24);
@ -272,14 +264,13 @@ int recvInt32LE(int socket, int32_t *data) {
} }
return -1; return -1;
} }
unsigned char in[ZLIB_CHUNK]; static unsigned char in[ZLIB_CHUNK];
unsigned char out[ZLIB_CHUNK]; static unsigned char out[ZLIB_CHUNK];
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) { static int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int retval = 0; int retval = 0;
@ -297,7 +288,7 @@ int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) {
int sock = socket(AF_INET,SOCK_STREAM,0); int sock = socket(AF_INET,SOCK_STREAM,0);
if (sock < 0) { if (sock < 0) {
socket_error("create connection socket"); socketError("create connection socket");
return -1; return -1;
} }
@ -307,7 +298,7 @@ int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) {
s.sin_port = htons(NETLOADER_SERVER_PORT); s.sin_port = htons(NETLOADER_SERVER_PORT);
s.sin_addr.s_addr = nxaddr; s.sin_addr.s_addr = nxaddr;
if (connect(sock,(struct sockaddr *)&s,sizeof(s)) < 0 ) { if (connect(sock,(struct sockaddr *)&s,sizeof(s)) < 0) {
struct in_addr address; struct in_addr address;
address.s_addr = nxaddr; address.s_addr = nxaddr;
fprintf(stderr,"Connection to %s failed\n",inet_ntoa(address)); fprintf(stderr,"Connection to %s failed\n",inet_ntoa(address));
@ -336,13 +327,13 @@ int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) {
int response; int response;
if(recvInt32LE(sock,&response)!=0) { if (recvInt32LE(sock,&response)!=0) {
fprintf(stderr,"Invalid response\n"); fprintf(stderr,"Invalid response\n");
retval = 1; retval = 1;
goto error; goto error;
} }
if(response!=0) { if (response!=0) {
switch(response) { switch(response) {
case -1: case -1:
fprintf(stderr,"Failed to create file\n"); fprintf(stderr,"Failed to create file\n");
@ -387,7 +378,7 @@ int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) {
goto error; goto error;
} }
if(sendData(sock,have,out)) { if (sendData(sock,have,out)) {
fprintf(stderr,"Failed sending %s\n", name); fprintf(stderr,"Failed sending %s\n", name);
retval = 1; retval = 1;
(void)deflateEnd(&strm); (void)deflateEnd(&strm);
@ -406,14 +397,14 @@ int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) {
printf("%zu sent (%.2f%%), %zd blocks\n",totalsent, (float)(totalsent * 100.0)/ filesize, blocks); printf("%zu sent (%.2f%%), %zd blocks\n",totalsent, (float)(totalsent * 100.0)/ filesize, blocks);
if(recvInt32LE(sock,&response)!=0) { if (recvInt32LE(sock,&response)!=0) {
fprintf(stderr,"Failed sending %s\n",name); fprintf(stderr,"Failed sending %s\n",name);
retval = 1; retval = 1;
goto error; goto error;
} }
if(sendData(sock,cmdlen+4,(unsigned char*)cmdbuf)) { if (sendData(sock,cmdlen+4,(unsigned char*)cmdbuf)) {
fprintf(stderr,"Failed sending command line\n"); fprintf(stderr,"Failed sending command line\n");
retval = 1; retval = 1;
@ -421,12 +412,12 @@ int sendNROFile(in_addr_t nxaddr, char *name, size_t filesize, FILE *fh) {
} }
error: error:
shutdownSocket(sock); shutdownSocket(sock, SHUT_WR);
return retval; return retval;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void showHelp() { static void showHelp() {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
puts("Usage: nxlink [options] nrofile\n"); puts("Usage: nxlink [options] nrofile\n");
puts("--help, -h Display this information"); puts("--help, -h Display this information");
@ -440,7 +431,7 @@ void showHelp() {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int add_extra_args(int len, char *buf, char *extra_args) { static int addExtraArgs(int len, char *buf, char *extra_args) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
if (NULL==extra_args) return len; if (NULL==extra_args) return len;
@ -457,7 +448,7 @@ int add_extra_args(int len, char *buf, char *extra_args) {
do { do {
c = *src++; c = *src++;
extra_len--; extra_len--;
} while(c ==' ' && extra_len >= 0); } while (c ==' ' && extra_len >= 0);
if (c == '\"' || c == '\'') { if (c == '\"' || c == '\'') {
int quote = c; int quote = c;
@ -465,7 +456,7 @@ int add_extra_args(int len, char *buf, char *extra_args) {
c = *src++; c = *src++;
if (c != quote) *dst++ = c; if (c != quote) *dst++ = c;
extra_len--; extra_len--;
} while(c != quote && extra_len >= 0); } while (c != quote && extra_len >= 0);
*dst++ = '\0'; *dst++ = '\0';
@ -475,16 +466,22 @@ int add_extra_args(int len, char *buf, char *extra_args) {
*dst++ = c; *dst++ = c;
extra_len--; extra_len--;
c = *src++; c = *src++;
} while(c != ' ' && extra_len >= 0); } while (c != ' ' && extra_len >= 0);
*dst++ = '\0'; *dst++ = '\0';
} while(extra_len >= 0); } while (extra_len >= 0);
return dst - buf; return dst - buf;
} }
#define NRO_ARGS 1000 #define NRO_ARGS 1000
#ifdef __WIN32__
static void win32_socket_cleanup(void) {
WSACleanup();
}
#endif
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int main(int argc, char **argv) { int main(int argc, char **argv) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -498,10 +495,10 @@ int main(int argc, char **argv) {
if (argc < 2) { if (argc < 2) {
showHelp(); showHelp();
return 1; return EXIT_FAILURE;
} }
while(1) { while (1) {
static struct option long_options[] = { static struct option long_options[] = {
{"address", required_argument, 0, 'a'}, {"address", required_argument, 0, 'a'},
{"retries", required_argument, 0, 'r'}, {"retries", required_argument, 0, 'r'},
@ -543,7 +540,7 @@ int main(int argc, char **argv) {
break; break;
case 'h': case 'h':
showHelp(); showHelp();
return 1; return EXIT_FAILURE;
case NRO_ARGS: case NRO_ARGS:
extra_args=optarg; extra_args=optarg;
break; break;
@ -554,7 +551,7 @@ int main(int argc, char **argv) {
char *filename = argv[optind++]; char *filename = argv[optind++];
if (filename== NULL) { if (filename== NULL) {
showHelp(); showHelp();
return 1; return EXIT_FAILURE;
} }
memset(cmdbuf, '\0', sizeof(cmdbuf)); memset(cmdbuf, '\0', sizeof(cmdbuf));
@ -562,7 +559,7 @@ int main(int argc, char **argv) {
FILE *fh = fopen(filename,"rb"); FILE *fh = fopen(filename,"rb");
if (fh == NULL) { if (fh == NULL) {
fprintf(stderr,"Failed to open %s\n",filename); fprintf(stderr,"Failed to open %s\n",filename);
return -1; return EXIT_FAILURE;
} }
#ifdef _WIN32 #ifdef _WIN32
@ -574,7 +571,7 @@ int main(int argc, char **argv) {
fseek(fh,0,SEEK_SET); fseek(fh,0,SEEK_SET);
char *basename = NULL; char *basename = NULL;
if((basename=strrchr(filename,'/'))!=NULL) { if ((basename=strrchr(filename,'/'))!=NULL) {
basename++; basename++;
} else if ((basename=strrchr(filename,'\\'))!=NULL) { } else if ((basename=strrchr(filename,'\\'))!=NULL) {
basename++; basename++;
@ -582,7 +579,7 @@ int main(int argc, char **argv) {
basename = filename; basename = filename;
} }
if(basepath) { if (basepath) {
size_t finalpath_len = strlen(basepath); size_t finalpath_len = strlen(basepath);
if (basepath[finalpath_len] == '/') { if (basepath[finalpath_len] == '/') {
finalpath_len += (strlen(basename) + 1); finalpath_len += (strlen(basename) + 1);
@ -599,12 +596,12 @@ int main(int argc, char **argv) {
for (int index = optind; index < argc; index++) { for (int index = optind; index < argc; index++) {
int len=strlen(argv[index]); int len=strlen(argv[index]);
if ( (cmdlen + len + 5 ) >= (sizeof(cmdbuf) - 2) ) break; if ((cmdlen + len + 5) >= (sizeof(cmdbuf) - 2)) break;
strcpy(&cmdbuf[cmdlen+4],argv[index]); strcpy(&cmdbuf[cmdlen+4],argv[index]);
cmdlen+= len + 1; cmdlen+= len + 1;
} }
cmdlen = add_extra_args(cmdlen, &cmdbuf[4], extra_args); cmdlen = addExtraArgs(cmdlen, &cmdbuf[4], extra_args);
cmdbuf[0] = cmdlen & 0xff; cmdbuf[0] = cmdlen & 0xff;
cmdbuf[1] = (cmdlen>>8) & 0xff; cmdbuf[1] = (cmdlen>>8) & 0xff;
@ -615,8 +612,9 @@ int main(int argc, char **argv) {
WSADATA wsa_data; WSADATA wsa_data;
if (WSAStartup (MAKEWORD(2,2), &wsa_data)) { if (WSAStartup (MAKEWORD(2,2), &wsa_data)) {
printf ("WSAStartup failed\n"); printf ("WSAStartup failed\n");
return 1; return EXIT_FAILURE;
} }
atexit(&win32_socket_cleanup);
#endif #endif
struct in_addr nxaddr; struct in_addr nxaddr;
@ -627,7 +625,7 @@ int main(int argc, char **argv) {
if (nxaddr.s_addr == INADDR_NONE) { if (nxaddr.s_addr == INADDR_NONE) {
printf("No response from Switch!\n"); printf("No response from Switch!\n");
return 1; return EXIT_FAILURE;
} }
} else { } else {
@ -640,14 +638,19 @@ int main(int argc, char **argv) {
if (nxaddr.s_addr == INADDR_NONE) { if (nxaddr.s_addr == INADDR_NONE) {
fprintf(stderr,"Invalid address\n"); fprintf(stderr,"Invalid address\n");
return 1; return EXIT_FAILURE;
} }
int res = sendNROFile(nxaddr.s_addr,finalpath,filesize,fh); int res = sendNROFile(nxaddr.s_addr,finalpath,filesize,fh);
fclose(fh); fclose(fh);
if ( res == 0 && server) { if (res != 0)
return EXIT_FAILURE;
if (!server)
return EXIT_SUCCESS;
printf("starting server\n"); printf("starting server\n");
struct sockaddr_in serv_addr; struct sockaddr_in serv_addr;
@ -658,97 +661,74 @@ int main(int argc, char **argv) {
serv_addr.sin_port = htons(NETLOADER_CLIENT_PORT); serv_addr.sin_port = htons(NETLOADER_CLIENT_PORT);
int listenfd = socket(AF_INET, SOCK_STREAM, 0); int listenfd = socket(AF_INET, SOCK_STREAM, 0);
int rc; if (listenfd < 0) {
socketError("socket");
return EXIT_FAILURE;
}
if(listenfd < 0) { int rc = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
socket_error("socket"); if (rc != 0) {
} else { socketError("bind listen socket");
shutdownSocket(listenfd, 0);
return EXIT_FAILURE;
}
rc = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); rc = setSocketNonblocking(listenfd);
if (rc == -1) {
socketError("listen fcntl");
shutdownSocket(listenfd, 0);
return EXIT_FAILURE;
}
if(rc != 0) {
socket_error("bind listen socket");
} else {
if (set_socket_nonblocking(listenfd) == -1) {
socket_error("listen fcntl");
} else {
rc = listen(listenfd, 10); rc = listen(listenfd, 10);
if (rc != 0) {
socketError("listen");
shutdownSocket(listenfd, 0);
return EXIT_FAILURE;
}
if(rc != 0) {
socket_error("listen");
} else {
printf("server active ...\n"); printf("server active ...\n");
int datafd = -1; int datafd = -1;
while( listenfd != -1 || datafd != -1) { while (listenfd != -1 || datafd != -1) {
struct sockaddr_in sa_remote; struct sockaddr_in sa_remote;
if(listenfd >= 0 && datafd < 0) {
if (pollSocket(listenfd >= 0 ? listenfd : datafd, POLLIN, -1))
break;
if (listenfd >= 0) {
socklen_t addrlen = sizeof(sa_remote); socklen_t addrlen = sizeof(sa_remote);
datafd = accept(listenfd, (struct sockaddr*)&sa_remote, &addrlen); datafd = accept(listenfd, (struct sockaddr*)&sa_remote, &addrlen);
if(datafd < 0) { if (datafd < 0 && socketError("accept") != EWOULDBLOCK)
#ifdef _WIN32 break;
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
socket_error("accept");
listenfd = -1;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
socket_error("accept");
listenfd = -1;
}
#endif
if (datafd >= 0) {
shutdownSocket(listenfd, 0);
listenfd = -1;
}
} else { } else {
shutdownSocket(listenfd);
listenfd = -1;
}
}
if(datafd >= 0) {
char recvbuf[256]; char recvbuf[256];
int len = recv(datafd,recvbuf,256,0); int len = recv(datafd, recvbuf, sizeof(recvbuf), 0);
if (len > 0 ) { if (len == 0 || (len < 0 && socketError("recv") != EWOULDBLOCK)) {
recvbuf[len] = 0; shutdownSocket(datafd, 0);
printf("%s", recvbuf);
} else {
if (len == -1) {
#ifdef _WIN32
int errcode = WSAGetLastError();
if (errcode != WSAEWOULDBLOCK) {
socket_error("recvdata");
len = 0;
}
#else
if ( errno != EWOULDBLOCK && errno != EAGAIN) {
perror("recvdata");
len = 0;
}
#endif
}
if (len ==0 ) {
shutdownSocket(datafd);
datafd = -1; datafd = -1;
break;
}
if (len > 0)
fwrite(recvbuf, 1, len, stdout);
} }
} }
}
} if (listenfd >= 0)
shutdownSocket(listenfd, 0);
if (datafd >= 0)
shutdownSocket(datafd, SHUT_RD);
printf("exiting ... \n"); printf("exiting ... \n");
}
}
}
}
}
#ifdef __WIN32__ return EXIT_SUCCESS;
WSACleanup ();
#endif
return res;
} }

View File

@ -410,9 +410,7 @@ size_t build_romfs_into_file(filepath_t *in_dirpath, FILE *f_out, off_t base_off
cur_entry->name_size = name_size; cur_entry->name_size = name_size;
memcpy(cur_entry->name, cur_dir->cur_path.char_path + 1, name_size); memcpy(cur_entry->name, cur_dir->cur_path.char_path + 1, name_size);
romfs_dirent_ctx_t *temp = cur_dir;
cur_dir = cur_dir->next; cur_dir = cur_dir->next;
free(temp);
} }
header.header_size = le_dword(sizeof(header)); header.header_size = le_dword(sizeof(header));
@ -474,11 +472,27 @@ size_t build_romfs_into_file(filepath_t *in_dirpath, FILE *f_out, off_t base_off
os_fclose(f_in); os_fclose(f_in);
cur_file = cur_file->next;
}
free(buffer);
/* Free all files. */
cur_file = romfs_ctx.files;
while (cur_file != NULL) {
romfs_fent_ctx_t *temp = cur_file; romfs_fent_ctx_t *temp = cur_file;
cur_file = cur_file->next; cur_file = cur_file->next;
free(temp); free(temp);
} }
free(buffer); romfs_ctx.files = NULL;
/* Free all directories. */
cur_dir = root_ctx;
while (cur_dir != NULL) {
romfs_dirent_ctx_t *temp = cur_dir;
cur_dir = cur_dir->next;
free(temp);
}
root_ctx = NULL;
fseeko64(f_out, base_offset + dir_hash_table_ofs, SEEK_SET); fseeko64(f_out, base_offset + dir_hash_table_ofs, SEEK_SET);
if (fwrite(dir_hash_table, 1, romfs_ctx.dir_hash_table_size, f_out) != romfs_ctx.dir_hash_table_size) { if (fwrite(dir_hash_table, 1, romfs_ctx.dir_hash_table_size, f_out) != romfs_ctx.dir_hash_table_size) {