Skip to content

Commit

Permalink
Update to v0.3.
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkMatterCore committed Jun 15, 2020
1 parent 4eeb1f5 commit 8832eec
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 58 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ Usage:
--------------

```
wad2bin <keys file> <device.cert> <input WAD> <output dir>
%s <keys file> <device.cert> <input WAD> <output dir> [parent title ID]
Paths must not exceed 1023 characters. Relative paths are supported.
The required directory tree for the *.bin file(s) will be created at the output directory.
You can set your SD card root directory as the output directory.
Parent title ID is only required if the input WAD is a DLC. A 16 character long hex string is expected.
```

Differences between `content.bin` files and `<index>.bin` files:
Expand Down Expand Up @@ -51,6 +52,10 @@ wad2bin is licensed under GPLv3 or (at your option) any later version.
Changelog:
--------------

**v0.3:**

Force the user to provide the full parent title ID for DLC WADs.

**v0.2:**

Added proper support for DLC WADs, even if they're incomplete (e.g. full TMD with missing content files).
Expand Down
11 changes: 2 additions & 9 deletions source/bin.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ bool binGenerateContentBinFromUnpackedInstallableWadPackage(os_char_t *unpacked_
return success;
}

bool binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(os_char_t *unpacked_wad_path, os_char_t *out_path, u8 *tmd, size_t tmd_size)
bool binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(os_char_t *unpacked_wad_path, os_char_t *out_path, u8 *tmd, size_t tmd_size, u64 parent_tid)
{
size_t unpacked_wad_path_len = 0;
size_t out_path_len = 0, new_out_path_len = 0;
Expand All @@ -447,9 +447,6 @@ bool binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(os_char_t *unpa
u64 title_id = 0;
char tid_lower_ascii[5] = {0};

u64 parent_tid = 0;
u32 parent_tid_lower = 0;

WadBackupPackageHeader bk_header = {0};
size_t res = 0;

Expand All @@ -468,10 +465,6 @@ bool binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(os_char_t *unpa
title_id = bswap_64(tmd_common_block->title_id);
utilsGenerateAsciiStringFromTitleIdLower(title_id, tid_lower_ascii);

/* Generate parent Title ID. */
parent_tid_lower = ((TITLE_LOWER(title_id) & 0xFFFFFF) | ((u32)(tid_lower_ascii[0] - 0x20) << 24));
parent_tid = TITLE_ID((tid_lower_ascii[0] == 'r' || tid_lower_ascii[0] == 's') ? TITLE_TYPE_DISC_GAME : TITLE_TYPE_DOWNLOADABLE_CHANNEL, parent_tid_lower);

/* Create directory tree. */
os_snprintf(out_path + out_path_len, MAX_PATH - out_path_len, PRIVATE_PATH("data"), tid_lower_ascii);
utilsCreateDirectoryTree(out_path);
Expand Down Expand Up @@ -535,7 +528,7 @@ bool binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(os_char_t *unpa
printf(" TMD size: 0x%" PRIx32 ".\n", bk_header.content_tmd_size);
printf(" Content data size: 0x%" PRIx32 ".\n", bk_header.content_data_size);
printf(" Backup area size: 0x%" PRIx32 ".\n", bk_header.backup_area_size);
printf(" Title ID: 0x%" PRIx64 ".\n\n", bk_header.title_id);
printf(" Title ID: %016" PRIx64 ".\n\n", bk_header.title_id);

/* Byteswap backup WAD header fields. */
wadByteswapBackupPackageHeaderFields(&bk_header);
Expand Down
4 changes: 2 additions & 2 deletions source/bin.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ typedef struct {
/// Generates a content.bin file using an unpacked WAD data directory and TMD data loaded into memory.
bool binGenerateContentBinFromUnpackedInstallableWadPackage(os_char_t *unpacked_wad_path, os_char_t *out_path, u8 *tmd, size_t tmd_size);

/// Generates <index>.bin file(s) using an unpacked WAD data directory and TMD data loaded into memory.
bool binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(os_char_t *unpacked_wad_path, os_char_t *out_path, u8 *tmd, size_t tmd_size);
/// Generates <index>.bin file(s) using an unpacked WAD data directory, TMD data loaded into memory and a parent title ID.
bool binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(os_char_t *unpacked_wad_path, os_char_t *out_path, u8 *tmd, size_t tmd_size, u64 parent_tid);

#endif /* __BIN_H__ */
72 changes: 36 additions & 36 deletions source/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ static bool g_keyDataLoaded = false, g_deviceCertRetrieved = false;

static int keysGetKeyAndValueFromFile(FILE *f, char **key, char **value);
static char keysConvertHexCharToBinary(char c);
static bool keysParseHexKey(u8 *out, const char *key, const char *value, u32 size);
static bool keysReadKeysFromFile(const os_char_t *keys_file_path);

static bool keysReadDeviceCertificateFromFile(const os_char_t *device_cert_path);
Expand Down Expand Up @@ -227,6 +226,41 @@ CertSigEcc480PubKeyEcc480 *keysGetDeviceCertificate(void)
return (g_deviceCertRetrieved ? &g_deviceCert : NULL);
}

bool keysParseHexKey(u8 *out, const char *key, const char *value, u32 size, bool verbose)
{
u32 hex_str_len = (2 * size);
size_t value_len = 0;

if (!out || (verbose && (!key || !strlen(key))) || !value || !(value_len = strlen(value)) || !size)
{
ERROR_MSG("Invalid parameters!");
return false;
}

if (value_len != hex_str_len)
{
if (verbose) ERROR_MSG("Key \"%s\" must be %u hex digits long!", key, hex_str_len);
return false;
}

memset(out, 0, size);

for(u32 i = 0; i < hex_str_len; i++)
{
char val = keysConvertHexCharToBinary(value[i]);
if (val == 'z')
{
if (verbose) ERROR_MSG("Invalid hex character in key \"%s\" at position %u!", key, i);
return false;
}

if ((i & 1) == 0) val <<= 4;
out[i >> 1] |= val;
}

return true;
}

/**
* Reads a line from file f and parses out the key and value from it.
* The format of a line must match /^ *[A-Za-z0-9_] *[,=] *.+$/.
Expand Down Expand Up @@ -373,40 +407,6 @@ static char keysConvertHexCharToBinary(char c)
return 'z';
}

static bool keysParseHexKey(u8 *out, const char *key, const char *value, u32 size)
{
if (!out || !key || !strlen(key) || !value || !strlen(value) || !size)
{
ERROR_MSG("Invalid parameters!");
return false;
}

u32 hex_str_len = (2 * size);

if (strlen(value) != hex_str_len)
{
ERROR_MSG("Key \"%s\" must be %u hex digits long!", key, hex_str_len);
return false;
}

memset(out, 0, size);

for(u32 i = 0; i < hex_str_len; i++)
{
char val = keysConvertHexCharToBinary(value[i]);
if (val == 'z')
{
ERROR_MSG("Invalid hex character in key \"%s\" at position %u!", key, i);
return false;
}

if ((i & 1) == 0) val <<= 4;
out[i >> 1] |= val;
}

return true;
}

static bool keysReadKeysFromFile(const os_char_t *keys_file_path)
{
if (!keys_file_path || !os_strlen(keys_file_path))
Expand Down Expand Up @@ -442,7 +442,7 @@ static bool keysReadKeysFromFile(const os_char_t *keys_file_path)
if (g_keyData[i].retrieved || strlen(key) != strlen(g_keyData[i].name) || strcmp(key, g_keyData[i].name) != 0) continue;

/* Parse current key. */
if ((parse_fail = !keysParseHexKey(g_keyData[i].key, key, value, g_keyData[i].key_size)))
if ((parse_fail = !keysParseHexKey(g_keyData[i].key, key, value, g_keyData[i].key_size, true)))
{
/* Reset flag if we're not dealing with a mandatory key. */
if (!g_keyData[i].mandatory) parse_fail = false;
Expand Down
3 changes: 3 additions & 0 deletions source/keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ u8 *keysGetPrngKey(void);
u8 *keysGetEccPrivateKey(void);
CertSigEcc480PubKeyEcc480 *keysGetDeviceCertificate(void);

/// Parses binary data from a hex string.
bool keysParseHexKey(u8 *out, const char *key, const char *value, u32 size, bool verbose);

#endif /* __KEYS_H__ */
53 changes: 44 additions & 9 deletions source/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int main(int argc, char **argv)

/* Reserve memory for an extra temporary path. */
os_char_t *paths[ARG_COUNT + 1] = {0};
u64 parent_tid = 0;

u8 *cert_chain = NULL;
size_t cert_chain_size = 0;
Expand All @@ -48,12 +49,14 @@ int main(int argc, char **argv)
printf("\nwad2bin v%s (c) DarkMatterCore.\n", VERSION);
printf("Built: %s %s.\n\n", __TIME__, __DATE__);

if (argc != (ARG_COUNT + 1) || strlen(argv[1]) >= MAX_PATH || strlen(argv[2]) >= MAX_PATH || strlen(argv[3]) >= MAX_PATH || (strlen(argv[4]) + SD_CONTENT_PATH_MAX_LENGTH) >= MAX_PATH)
if (argc < (ARG_COUNT + 1) || argc > (ARG_COUNT + 2) || strlen(argv[1]) >= MAX_PATH || strlen(argv[2]) >= MAX_PATH || strlen(argv[3]) >= MAX_PATH || \
(strlen(argv[4]) + SD_CONTENT_PATH_MAX_LENGTH) >= MAX_PATH || (argc == (ARG_COUNT + 2) && strlen(argv[5]) != 16))
{
printf("Usage: %s <keys file> <device.cert> <input WAD> <output dir>\n\n", argv[0]);
printf("Usage: %s <keys file> <device.cert> <input WAD> <output dir> [parent title ID]\n\n", argv[0]);
printf("Paths must not exceed %u characters. Relative paths are supported.\n", MAX_PATH - 1);
printf("The required directory tree for the *.bin file(s) will be created at the output directory.\n");
printf("You can set your SD card root directory as the output directory.\n");
printf("Parent title ID is only required if the input WAD is a DLC. A 16 character long hex string is expected.\n");
ret = -1;
goto out;
}
Expand Down Expand Up @@ -97,10 +100,34 @@ int main(int argc, char **argv)
}
}

/* Check if the user provided a parent title ID. */
if (argc == (ARG_COUNT + 2))
{
/* Parse parent title ID. */
if (!keysParseHexKey((u8*)&parent_tid, NULL, argv[5], 8, false))
{
printf("Failed to parse parent title ID!\n");
ret = -4;
goto out;
}

/* Byteswap parent title ID. */
parent_tid = bswap_64(parent_tid);

/* Check if the TID upper u32 is valid. */
u32 parent_tid_upper = TITLE_UPPER(parent_tid);
if (parent_tid_upper != TITLE_TYPE_DISC_GAME && parent_tid_upper != TITLE_TYPE_DOWNLOADABLE_CHANNEL && parent_tid_upper != TITLE_TYPE_DISC_BASED_CHANNEL)
{
printf("Invalid parent title ID category!\nOnly disc-based game IDs, downloadable channel IDs and disc-based channel IDs are supported.\n");
ret = -5;
goto out;
}
}

/* Load keydata and device certificate. */
if (!keysLoadKeyDataAndDeviceCert(paths[0], paths[1]))
{
ret = -4;
ret = -6;
goto out;
}

Expand All @@ -109,7 +136,7 @@ int main(int argc, char **argv)
/* Unpack input WAD package. */
if (!wadUnpackInstallablePackage(paths[2], paths[4], &cert_chain, &cert_chain_size, &ticket, &ticket_size, &tmd, &tmd_size, dec_titlekey, &tid_upper))
{
ret = -5;
ret = -7;
goto out;
}

Expand All @@ -121,23 +148,31 @@ int main(int argc, char **argv)
if (!utilsAlignBuffer((void**)&tmd, &aligned_tmd_size, WAD_BLOCK_SIZE))
{
printf("Failed to align TMD buffer to WAD block size!\n");
ret = -6;
ret = -8;
goto out;
}

if (tid_upper == TITLE_TYPE_DLC)
{
/* Check if a parent title ID was provided. */
if (argc != (ARG_COUNT + 2))
{
printf("Error: parent title ID not provided! This is required for DLC titles.\n");
ret = -9;
goto out;
}

/* Generate <index>.bin file(s). */
if (!binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(paths[4], paths[3], tmd, tmd_size))
if (!binGenerateIndexedPackagesFromUnpackedInstallableWadPackage(paths[4], paths[3], tmd, tmd_size, parent_tid))
{
ret = -7;
ret = -10;
goto out;
}
} else {
/* Generate content.bin file. */
if (!binGenerateContentBinFromUnpackedInstallableWadPackage(paths[4], paths[3], tmd, tmd_size))
{
ret = -8;
ret = -11;
goto out;
}
}
Expand All @@ -158,7 +193,7 @@ int main(int argc, char **argv)
if (cert_chain) free(cert_chain);

/* Remove unpacked WAD directory. */
utilsRemoveDirectoryRecursively(paths[4]);
if (paths[4]) utilsRemoveDirectoryRecursively(paths[4]);

for(u32 i = 0; i <= ARG_COUNT; i++)
{
Expand Down
2 changes: 1 addition & 1 deletion source/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include "types.h"
#include "os.h"

#define VERSION "0.2"
#define VERSION "0.3"

/* Supress clang warnings about variadic macro arguments. */
#ifdef __clang__
Expand Down

0 comments on commit 8832eec

Please sign in to comment.