mirror of
https://github.com/GerbilSoft/rvthtool.git
synced 2025-06-18 11:35:33 -04:00
[wadresign] Initial 'resign' command implementation.
Currently writes the WAD header and certificate chain, including the extra debug certificate if recrypting as debug. TODO: - Write the extra debug certificate when recrypting discs. - Verify certificate chain ordering for discs. For WADs, it's CA, ticket, TMD, dev; for discs, it seems to be ticket, CA, TMD. - Convert early WAD headers to final. This also requires converting the name, if present, to a footer.
This commit is contained in:
parent
6088b79dde
commit
f09261ae7b
15
NEWS.md
15
NEWS.md
@ -1,5 +1,20 @@
|
||||
# Changes
|
||||
|
||||
## v2.0 - GUI Release (released 2019/??/??)
|
||||
|
||||
New features:
|
||||
* GUI frontend written using the Qt Toolkit.
|
||||
* [TODO] Added device querying on Windows.
|
||||
* wadresign: Command line tool to recrypt and resign WAD files.
|
||||
* Can read retail, Korean, debug, and early devkit WADs.
|
||||
* Can create retail (fakesigned), Korean (fakesigned), and debug
|
||||
(realsigned) WAD files.
|
||||
* **WARNING:** Use with caution if converting system titles for use
|
||||
on real hardware.
|
||||
|
||||
Low-level changes:
|
||||
* Rewrote librvth using C++ to improve maintainability.
|
||||
|
||||
## v1.1.1 - Brown Paper Bag Release (released 2018/09/17)
|
||||
|
||||
* Extracting images broke because it failed when opening a file with 0 bytes
|
||||
|
12
README.md
12
README.md
@ -122,3 +122,15 @@ install on retail consoles, since they're encrypted with debug keys.
|
||||
Do **NOT** attempt to install them by re-encrypting them with the retail
|
||||
keys. Doing so will most likely result in a brick, especially if the 128 MB
|
||||
mode IOS WADs are used.
|
||||
|
||||
## wadresign
|
||||
|
||||
This is a command line tool to resign WAD files to and from any Wii keyset.
|
||||
Conversion to Debug will be realsigned. Conversion to retail or Korean will
|
||||
be fakesigned.
|
||||
|
||||
In addition, this tool supports reading early devkit WADs, which makes it
|
||||
possible to convert them to run on emulators and/or later devkits.
|
||||
|
||||
**WARNING:** Use with caution if converting a system title for installation
|
||||
on read hardware, since this may result in an unrecoverable brick.
|
||||
|
@ -610,6 +610,9 @@ int RvtH::recryptWiiPartitions(unsigned int bank,
|
||||
|
||||
// Certificate chain order for retail is Ticket, CA, TMD.
|
||||
// TODO: Verify for debug! (and write the dev cert?)
|
||||
// NOTE: WAD cert chain order is CA, Ticket, TMD...
|
||||
// (CA, Ticket, TMD, Dev for debug)
|
||||
// TODO: Verify all of this.
|
||||
p_cert_chain = &hdr_new.u8[data_pos];
|
||||
memcpy(p_cert_chain, cert_ticket, sizeof(*cert_ticket));
|
||||
p_cert_chain += sizeof(*cert_ticket);
|
||||
|
@ -10,11 +10,13 @@ SET(wadresign_SRCS
|
||||
main.c
|
||||
print-info.c
|
||||
wad-fns.c
|
||||
resign-wad.c
|
||||
)
|
||||
# Headers.
|
||||
SET(wadresign_H
|
||||
print-info.h
|
||||
wad-fns.h
|
||||
resign-wad.h
|
||||
)
|
||||
IF(WIN32)
|
||||
SET(wadresign_RC resource.rc)
|
||||
|
@ -36,6 +36,7 @@
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#include "print-info.h"
|
||||
#include "resign-wad.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define RVTH_CDECL __cdecl
|
||||
@ -89,6 +90,11 @@ static void print_help(const TCHAR *argv0)
|
||||
"info file.wad\n"
|
||||
"- Print information about the specified WAD file.\n"
|
||||
"\n"
|
||||
"resign source.wad dest.wad\n"
|
||||
" - Resigns source.wad and creates dest.wad using the new key.\n"
|
||||
" Default converts Retail/Korean WADs to Debug, and\n"
|
||||
" Debug WADs to Retail.\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\n"
|
||||
" -k, --recrypt=KEY Recrypt the WAD using the specified KEY:\n"
|
||||
@ -196,9 +202,20 @@ int RVTH_CDECL _tmain(int argc, TCHAR *argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
ret = print_wad_info(argv[optind+1]);
|
||||
} else if (!_tcscmp(argv[optind], _T("resign"))) {
|
||||
// Resign a WAD.
|
||||
if (argc < optind+2) {
|
||||
print_error(argv[0], _T("WAD filenames not specified"));
|
||||
return EXIT_FAILURE;
|
||||
} else if (argc < optind+3) {
|
||||
print_error(argv[0], _T("Output WAD filename not specified"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
ret = resign_wad(argv[optind+1], argv[optind+2], recrypt_key);
|
||||
} else {
|
||||
// If the "command" contains a slash or dot (or backslash on Windows),
|
||||
// assume it's a filename and handle it as 'info'.
|
||||
// TODO: If two filenames are specified, handle it as 'resign'.
|
||||
const TCHAR *p;
|
||||
bool isFilename = false;
|
||||
for (p = argv[optind]; *p != 0; p++) {
|
||||
|
@ -120,11 +120,12 @@ const char *identify_wad_type(const uint8_t *buf, size_t buf_len, bool *pIsEarly
|
||||
}
|
||||
|
||||
/**
|
||||
* 'info' command.
|
||||
* @param wad_filename WAD filename.
|
||||
* 'info' command. (internal function)
|
||||
* @param f_wad WAD file.
|
||||
* @param wad_filename WAD filename. (for error messages)
|
||||
* @return 0 on success; negative POSIX error code or positive ID code on error.
|
||||
*/
|
||||
int print_wad_info(const TCHAR *wad_filename)
|
||||
int print_wad_info_FILE(FILE *f_wad, const TCHAR *wad_filename)
|
||||
{
|
||||
int ret;
|
||||
size_t size;
|
||||
@ -143,17 +144,8 @@ int print_wad_info(const TCHAR *wad_filename)
|
||||
const char *issuer_ticket, *issuer_tmd;
|
||||
RVL_SigStatus_e sig_status_ticket, sig_status_tmd;
|
||||
|
||||
// Open the WAD file.
|
||||
FILE *f_wad = _tfopen(wad_filename, _T("rb"));
|
||||
if (!f_wad) {
|
||||
int err = errno;
|
||||
fputs("*** ERROR opening WAD file '", stderr);
|
||||
_fputts(wad_filename, stderr);
|
||||
fprintf(stderr, "': %s\n", strerror(err));
|
||||
return -err;
|
||||
}
|
||||
|
||||
// Read the WAD header.
|
||||
rewind(f_wad);
|
||||
size = fread(&header, 1, sizeof(header), f_wad);
|
||||
if (size != sizeof(header)) {
|
||||
int err = errno;
|
||||
@ -292,6 +284,30 @@ int print_wad_info(const TCHAR *wad_filename)
|
||||
|
||||
end:
|
||||
free(tmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'info' command.
|
||||
* @param wad_filename WAD filename.
|
||||
* @return 0 on success; negative POSIX error code or positive ID code on error.
|
||||
*/
|
||||
int print_wad_info(const TCHAR *wad_filename)
|
||||
{
|
||||
int ret;
|
||||
|
||||
// Open the WAD file.
|
||||
FILE *f_wad = _tfopen(wad_filename, _T("rb"));
|
||||
if (!f_wad) {
|
||||
int err = errno;
|
||||
fputs("*** ERROR opening WAD file '", stderr);
|
||||
_fputts(wad_filename, stderr);
|
||||
fprintf(stderr, "': %s\n", strerror(err));
|
||||
return -err;
|
||||
}
|
||||
|
||||
// Print the WAD info.
|
||||
ret = print_wad_info_FILE(f_wad, wad_filename);
|
||||
fclose(f_wad);
|
||||
return ret;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
#define __RVTHTOOL_WADRESIGN_PRINT_INFO_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "librvth/tcharx.h"
|
||||
|
||||
@ -54,6 +54,14 @@ const char *issuer_type(RVL_Cert_Issuer issuer);
|
||||
*/
|
||||
const char *identify_wad_type(const uint8_t *buf, size_t buf_len, bool *pIsEarly);
|
||||
|
||||
/**
|
||||
* 'info' command. (internal function)
|
||||
* @param f_wad Opened WAD file.
|
||||
* @param wad_filename WAD filename. (for error messages)
|
||||
* @return 0 on success; negative POSIX error code or positive ID code on error.
|
||||
*/
|
||||
int print_wad_info_FILE(FILE *f_wad, const TCHAR *wad_filename);
|
||||
|
||||
/**
|
||||
* 'info' command.
|
||||
* @param wad_filename WAD filename.
|
||||
|
363
src/wadresign/resign-wad.c
Normal file
363
src/wadresign/resign-wad.c
Normal file
@ -0,0 +1,363 @@
|
||||
/***************************************************************************
|
||||
* RVT-H Tool: WAD Resigner *
|
||||
* print-info.c: Print WAD information. *
|
||||
* *
|
||||
* Copyright (c) 2018-2019 by David Korth. *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the *
|
||||
* Free Software Foundation; either version 2 of the License, or (at your *
|
||||
* option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "resign-wad.h"
|
||||
#include "print-info.h"
|
||||
#include "wad-fns.h"
|
||||
|
||||
// libwiicrypto
|
||||
#include "libwiicrypto/byteswap.h"
|
||||
#include "libwiicrypto/cert.h"
|
||||
#include "libwiicrypto/wii_wad.h"
|
||||
#include "libwiicrypto/sig_tools.h"
|
||||
|
||||
// C includes.
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ISALNUM(c) isalnum((unsigned char)c)
|
||||
|
||||
typedef union _WAD_Header {
|
||||
Wii_WAD_Header wad;
|
||||
Wii_WAD_Header_EARLY wadE;
|
||||
} WAD_Header;
|
||||
|
||||
// Read buffer.
|
||||
typedef union _rdbuf_t {
|
||||
uint8_t u8[1024*1024];
|
||||
RVL_Ticket ticket;
|
||||
RVL_TMD_Header tmdHeader;
|
||||
} rdbuf_t;
|
||||
|
||||
/**
|
||||
* Align the file pointer to the next 64-byte boundary.
|
||||
* @param fp File pointer.
|
||||
*/
|
||||
static inline void fpAlign(FILE *fp)
|
||||
{
|
||||
int64_t offset = ftello(fp);
|
||||
if ((offset & 63) != 0) {
|
||||
offset = ALIGN(64, offset);
|
||||
fseeko(fp, offset, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 'resign' command.
|
||||
* @param src_wad [in] Source WAD.
|
||||
* @param dest_wad [in] Destination WAD.
|
||||
* @param recrypt_key [in] Key for recryption. (-1 for default)
|
||||
* @return 0 on success; negative POSIX error code or positive ID code on error.
|
||||
*/
|
||||
int resign_wad(const TCHAR *src_wad, const TCHAR *dest_wad, int recrypt_key)
|
||||
{
|
||||
int ret;
|
||||
size_t size;
|
||||
bool isEarly = false;
|
||||
WAD_Header header;
|
||||
WAD_Info_t wadInfo;
|
||||
RVL_CryptoType_e src_key;
|
||||
|
||||
// Files.
|
||||
FILE *f_src_wad = NULL, *f_dest_wad = NULL;
|
||||
|
||||
// Certificates.
|
||||
const RVL_Cert_RSA4096_RSA2048 *cert_CA;
|
||||
const RVL_Cert_RSA2048 *cert_ticket, *cert_TMD;
|
||||
const RVL_Cert_RSA2048_ECC *cert_dev;
|
||||
|
||||
// Read buffer.
|
||||
rdbuf_t *buf = NULL;
|
||||
|
||||
// Open the source WAD file.
|
||||
f_src_wad = _tfopen(src_wad, _T("rb"));
|
||||
if (!f_src_wad) {
|
||||
int err = errno;
|
||||
fputs("*** ERROR opening source WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "': %s\n", strerror(err));
|
||||
return -err;
|
||||
}
|
||||
|
||||
// Print the WAD information.
|
||||
ret = print_wad_info_FILE(f_src_wad, src_wad);
|
||||
if (ret != 0) {
|
||||
// Error printing the WAD information.
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Re-read the WAD header and parse the addresses.
|
||||
rewind(f_src_wad);
|
||||
size = fread(&header, 1, sizeof(header), f_src_wad);
|
||||
if (size != sizeof(header)) {
|
||||
int err = errno;
|
||||
fputs("*** ERROR reading WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "': %s\n", strerror(err));
|
||||
ret = -err;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Identify the WAD type.
|
||||
// TODO: More extensive error handling?
|
||||
// NOTE: Not saving the string, since we only need to knwo if
|
||||
// it's an early WAD or not.
|
||||
if (identify_wad_type((const uint8_t*)&header, sizeof(header), &isEarly) == NULL) {
|
||||
// Unrecognized WAD type.
|
||||
fputs("*** ERROR: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "' is not valid.\n");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Determine the sizes and addresses of various components.
|
||||
if (!isEarly) {
|
||||
ret = getWadInfo(&header.wad, &wadInfo);
|
||||
} else {
|
||||
ret = getWadInfo_early(&header.wadE, &wadInfo);
|
||||
}
|
||||
if (ret != 0) {
|
||||
// Unable to get WAD information.
|
||||
fputs("*** ERROR: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "' is not valid.");
|
||||
ret = 2;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Verify the ticket and TMD sizes.
|
||||
if (wadInfo.ticket_size != sizeof(RVL_Ticket)) {
|
||||
// Incorrect ticket size.
|
||||
fputs("*** ERROR: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "' ticket size is incorrect. (%u; should be %u)\n",
|
||||
wadInfo.ticket_size, (uint32_t)sizeof(RVL_Ticket));
|
||||
ret = 3;
|
||||
goto end;
|
||||
} else if (wadInfo.tmd_size < sizeof(RVL_TMD_Header)) {
|
||||
fputs("*** ERROR: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "' TMD size is too small. (%u; should be at least %u)\n",
|
||||
wadInfo.tmd_size, (uint32_t)sizeof(RVL_TMD_Header));
|
||||
ret = 4;
|
||||
goto end;
|
||||
} else if (wadInfo.tmd_size > 1*1024*1024) {
|
||||
// Too big.
|
||||
fputs("*** ERROR: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "' TMD size is too big. (%u; should be less than 1 MB)\n",
|
||||
wadInfo.tmd_size);
|
||||
ret = 5;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Allocate the memory buffer.
|
||||
buf = malloc(sizeof(*buf));
|
||||
if (!buf) {
|
||||
fputs("*** ERROR: Unable to allocate memory buffer.\n", stderr);
|
||||
ret = 6;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Load the ticket.
|
||||
fseek(f_src_wad, wadInfo.ticket_address, SEEK_SET);
|
||||
size = fread(&buf->ticket, 1, sizeof(buf->ticket), f_src_wad);
|
||||
if (size != sizeof(RVL_Ticket)) {
|
||||
// Read error.
|
||||
fputs("*** ERROR: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fputs("': Unable to read the ticket.\n", stderr);
|
||||
ret = 7;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Check the encryption key.
|
||||
switch (cert_get_issuer_from_name(buf->ticket.issuer)) {
|
||||
case RVL_CERT_ISSUER_RETAIL_TICKET:
|
||||
// Retail may be either Common Key or Korean Key.
|
||||
switch (buf->ticket.common_key_index) {
|
||||
case 0:
|
||||
src_key = RVL_CryptoType_Retail;
|
||||
break;
|
||||
case 1:
|
||||
src_key = RVL_CryptoType_Korean;
|
||||
break;
|
||||
default: {
|
||||
const char *s_key;
|
||||
// NOTE: A good number of retail WADs have an
|
||||
// incorrect common key index for some reason.
|
||||
fputs("*** WARNING: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fprintf(stderr, "': Invalid common key index %u.\n",
|
||||
buf->ticket.common_key_index);
|
||||
|
||||
if (buf->ticket.title_id.u8[7] == 'K') {
|
||||
s_key = "Korean";
|
||||
src_key = RVL_CryptoType_Korean;
|
||||
} else {
|
||||
s_key = "retail";
|
||||
src_key = RVL_CryptoType_Retail;
|
||||
}
|
||||
fprintf(stderr, "*** Assuming %s common key based on game ID.\n\n", s_key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RVL_CERT_ISSUER_DEBUG_TICKET:
|
||||
src_key = RVL_CryptoType_Debug;
|
||||
break;
|
||||
default:
|
||||
fputs("*** ERROR: WAD file '", stderr);
|
||||
_fputts(src_wad, stderr);
|
||||
fputs("': Unknown issuer.\n", stderr);
|
||||
ret = 8;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (recrypt_key == -1) {
|
||||
// Select the "opposite" key.
|
||||
switch (src_key) {
|
||||
case RVL_CryptoType_Retail:
|
||||
case RVL_CryptoType_Korean:
|
||||
recrypt_key = RVL_CryptoType_Debug;
|
||||
break;
|
||||
case RVL_CryptoType_Debug:
|
||||
recrypt_key = RVL_CryptoType_Retail;
|
||||
break;
|
||||
default:
|
||||
// Should not happen...
|
||||
assert(!"This shouldn't happen!");
|
||||
fputs("*** ERROR: Unable to select encryption key.\n", stderr);
|
||||
ret = 9;
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
if ((RVL_CryptoType_e)recrypt_key == src_key) {
|
||||
// No point in recrypting to the same key...
|
||||
fputs("*** ERROR: Cannot recrypt to the same key.\n", stderr);
|
||||
ret = 10;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
// Open the destination WAD file.
|
||||
f_dest_wad = _tfopen(dest_wad, _T("wb"));
|
||||
if (!f_dest_wad) {
|
||||
int err = errno;
|
||||
fputs("*** ERROR opening destination WAD file '", stderr);
|
||||
_fputts(dest_wad, stderr);
|
||||
fprintf(stderr, "' for write: %s\n", strerror(err));
|
||||
ret = -err;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// TODO: Convert early WAD header to final WAD.
|
||||
printf("Writing certificate chain...\n");
|
||||
|
||||
// Get the certificates.
|
||||
if (recrypt_key != RVL_CryptoType_Debug) {
|
||||
// Retail certificates.
|
||||
// Order: CA, Ticket, TMD
|
||||
cert_CA = (const RVL_Cert_RSA4096_RSA2048*)cert_get(RVL_CERT_ISSUER_DEBUG_CA);
|
||||
cert_ticket = (const RVL_Cert_RSA2048*)cert_get(RVL_CERT_ISSUER_DEBUG_TICKET);
|
||||
cert_TMD = (const RVL_Cert_RSA2048*)cert_get(RVL_CERT_ISSUER_DEBUG_TMD);
|
||||
cert_dev = NULL;
|
||||
header.wad.cert_chain_size = cpu_to_be32((uint32_t)(
|
||||
sizeof(*cert_CA) + sizeof(*cert_ticket) +
|
||||
sizeof(*cert_TMD)));
|
||||
} else {
|
||||
// Debug certificates.
|
||||
// Order: CA, Ticket, TMD
|
||||
cert_CA = (const RVL_Cert_RSA4096_RSA2048*)cert_get(RVL_CERT_ISSUER_DEBUG_CA);
|
||||
cert_ticket = (const RVL_Cert_RSA2048*)cert_get(RVL_CERT_ISSUER_DEBUG_TICKET);
|
||||
cert_TMD = (const RVL_Cert_RSA2048*)cert_get(RVL_CERT_ISSUER_DEBUG_TMD);
|
||||
cert_dev = (const RVL_Cert_RSA2048_ECC*)cert_get(RVL_CERT_ISSUER_DEBUG_DEV);
|
||||
header.wad.cert_chain_size = cpu_to_be32((uint32_t)(
|
||||
sizeof(*cert_CA) + sizeof(*cert_ticket) +
|
||||
sizeof(*cert_TMD) + sizeof(*cert_dev)));
|
||||
}
|
||||
|
||||
// Write the WAD header.
|
||||
size = fwrite(&header.wad, 1, sizeof(header.wad), f_dest_wad);
|
||||
if (size != sizeof(header.wad)) {
|
||||
int err = errno;
|
||||
fprintf(stderr, "*** ERROR writing WAD header: %s\n", strerror(err));
|
||||
ret = -err;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// 64-byte alignment.
|
||||
fpAlign(f_dest_wad);
|
||||
|
||||
// Write the certificates.
|
||||
size = fwrite(cert_CA, 1, sizeof(*cert_CA), f_dest_wad);
|
||||
if (size != sizeof(*cert_CA)) {
|
||||
int err = errno;
|
||||
fprintf(stderr, "*** ERROR writing WAD certificate chain: %s\n", strerror(err));
|
||||
ret = -err;
|
||||
goto end;
|
||||
}
|
||||
size = fwrite(cert_ticket, 1, sizeof(*cert_ticket), f_dest_wad);
|
||||
if (size != sizeof(*cert_ticket)) {
|
||||
int err = errno;
|
||||
fprintf(stderr, "*** ERROR writing WAD certificate chain: %s\n", strerror(err));
|
||||
ret = -err;
|
||||
goto end;
|
||||
}
|
||||
size = fwrite(cert_TMD, 1, sizeof(*cert_TMD), f_dest_wad);
|
||||
if (size != sizeof(*cert_TMD)) {
|
||||
int err = errno;
|
||||
fprintf(stderr, "*** ERROR writing WAD certificate chain: %s\n", strerror(err));
|
||||
ret = -err;
|
||||
goto end;
|
||||
}
|
||||
if (cert_dev) {
|
||||
size = fwrite(cert_dev, 1, sizeof(*cert_dev), f_dest_wad);
|
||||
if (size != sizeof(*cert_dev)) {
|
||||
int err = errno;
|
||||
fprintf(stderr, "*** ERROR writing WAD certificate chain: %s\n", strerror(err));
|
||||
ret = -err;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-byte alignment.
|
||||
fpAlign(f_dest_wad);
|
||||
|
||||
// TODO: Read and handle ticket, TMD, data, and footer/name.
|
||||
printf("Recrypting the ticket and TMD...\n");
|
||||
ret = 0;
|
||||
|
||||
end:
|
||||
free(buf);
|
||||
if (f_dest_wad) {
|
||||
// TODO: Delete if an error occurred?
|
||||
fclose(f_dest_wad);
|
||||
}
|
||||
if (f_src_wad) {
|
||||
fclose(f_src_wad);
|
||||
}
|
||||
return ret;
|
||||
}
|
44
src/wadresign/resign-wad.h
Normal file
44
src/wadresign/resign-wad.h
Normal file
@ -0,0 +1,44 @@
|
||||
/***************************************************************************
|
||||
* RVT-H Tool: WAD Resigner *
|
||||
* resign-wad.h: Re-sign a WAD file. *
|
||||
* *
|
||||
* Copyright (c) 2018-2019 by David Korth. *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the *
|
||||
* Free Software Foundation; either version 2 of the License, or (at your *
|
||||
* option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that 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/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef __RVTHTOOL_WADRESIGN_RESIGN_WAD_H__
|
||||
#define __RVTHTOOL_WADRESIGN_RESIGN_WAD_H__
|
||||
|
||||
#include "librvth/tcharx.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 'resign' command.
|
||||
* @param src_wad [in] Source WAD.
|
||||
* @param dest_wad [in] Destination WAD.
|
||||
* @param recrypt_key [in] Key for recryption. (-1 for default)
|
||||
* @return 0 on success; negative POSIX error code or positive ID code on error.
|
||||
*/
|
||||
int resign_wad(const TCHAR *src_wad, const TCHAR *dest_wad, int recrypt_key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RVTHTOOL_WADRESIGN_PRINT_INFO_H__ */
|
@ -19,7 +19,9 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "wad-fns.h"
|
||||
|
||||
#include "libwiicrypto/byteswap.h"
|
||||
#include "libwiicrypto/common.h"
|
||||
|
||||
/**
|
||||
* Get WAD info for a standard WAD file.
|
||||
|
Loading…
Reference in New Issue
Block a user