mirror of
https://github.com/GerbilSoft/rom-properties.git
synced 2025-06-18 19:45:41 -04:00
[libromdata] WiiTMD: New RomData parser for Wii and Wii U title metadata.
Possibly for other Nintendo systems too, e.g. DSi and 3DS. Currently displays the following fields: - Title ID - Issuer - Title version - OS version (if non-zero) (identifies IOS and IOSU) - Access rights (for Wii and Wii U only; but not Wii IOS) wii_structs.h: Some updates for TMD. wiiu_structs.h: Add CMD v1 structs. [xdg] Add WiiTMD with "application/x-nintendo-tmd".
This commit is contained in:
parent
c068c83522
commit
d59f9cefc0
@ -35,6 +35,7 @@ SET(${PROJECT_NAME}_SRCS
|
||||
Console/WiiCommon.cpp
|
||||
Console/WiiSave.cpp
|
||||
Console/WiiTicket.cpp
|
||||
Console/WiiTMD.cpp
|
||||
Console/WiiU.cpp
|
||||
Console/WiiWAD.cpp
|
||||
Console/WiiWAD_ops.cpp
|
||||
@ -167,6 +168,7 @@ SET(${PROJECT_NAME}_H
|
||||
Console/SufamiTurbo.hpp
|
||||
Console/WiiCommon.hpp
|
||||
Console/WiiTicket.hpp
|
||||
Console/WiiTMD.hpp
|
||||
Console/WiiSave.hpp
|
||||
Console/WiiU.hpp
|
||||
Console/WiiWAD.hpp
|
||||
|
429
src/libromdata/Console/WiiTMD.cpp
Normal file
429
src/libromdata/Console/WiiTMD.cpp
Normal file
@ -0,0 +1,429 @@
|
||||
/***************************************************************************
|
||||
* ROM Properties Page shell extension. (libromdata) *
|
||||
* WiiTMD.hpp: Nintendo Wii (and Wii U) title metadata reader. *
|
||||
* *
|
||||
* Copyright (c) 2016-2024 by David Korth. *
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later *
|
||||
***************************************************************************/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "WiiTMD.hpp"
|
||||
#include "wii_structs.h"
|
||||
#include "wiiu_structs.h"
|
||||
|
||||
// Other rom-properties libraries
|
||||
using namespace LibRpBase;
|
||||
using namespace LibRpFile;
|
||||
using namespace LibRpText;
|
||||
|
||||
// C++ STL classes
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace LibRomData {
|
||||
|
||||
class WiiTMDPrivate final : public RomDataPrivate
|
||||
{
|
||||
public:
|
||||
WiiTMDPrivate(const IRpFilePtr &file);
|
||||
|
||||
private:
|
||||
typedef RomDataPrivate super;
|
||||
RP_DISABLE_COPY(WiiTMDPrivate)
|
||||
|
||||
public:
|
||||
/** RomDataInfo **/
|
||||
static const char *const exts[];
|
||||
static const char *const mimeTypes[];
|
||||
static const RomDataInfo romDataInfo;
|
||||
|
||||
public:
|
||||
// TMD header
|
||||
RVL_TMD_Header tmdHeader;
|
||||
};
|
||||
|
||||
ROMDATA_IMPL(WiiTMD)
|
||||
|
||||
/** WiiTMDPrivate **/
|
||||
|
||||
/* RomDataInfo */
|
||||
const char *const WiiTMDPrivate::exts[] = {
|
||||
".tmd",
|
||||
|
||||
nullptr
|
||||
};
|
||||
const char *const WiiTMDPrivate::mimeTypes[] = {
|
||||
// Unofficial MIME types.
|
||||
// TODO: Get these upstreamed on FreeDesktop.org.
|
||||
"application/x-nintendo-tmd",
|
||||
|
||||
nullptr
|
||||
};
|
||||
const RomDataInfo WiiTMDPrivate::romDataInfo = {
|
||||
"WiiTMD", exts, mimeTypes
|
||||
};
|
||||
|
||||
WiiTMDPrivate::WiiTMDPrivate(const IRpFilePtr &file)
|
||||
: super(file, &romDataInfo)
|
||||
{
|
||||
// Clear the TMD header struct.
|
||||
memset(&tmdHeader, 0, sizeof(tmdHeader));
|
||||
}
|
||||
|
||||
/** WiiTMD **/
|
||||
|
||||
/**
|
||||
* Read a Nintendo Wii (or Wii U) ticket file. (.tik)
|
||||
*
|
||||
* A ROM image must be opened by the caller. The file handle
|
||||
* will be ref()'d and must be kept open in order to load
|
||||
* data from the ROM image.
|
||||
*
|
||||
* To close the file, either delete this object or call close().
|
||||
*
|
||||
* NOTE: Check isValid() to determine if this is a valid ROM.
|
||||
*
|
||||
* @param file Open ROM image.
|
||||
*/
|
||||
WiiTMD::WiiTMD(const IRpFilePtr &file)
|
||||
: super(new WiiTMDPrivate(file))
|
||||
{
|
||||
RP_D(WiiTMD);
|
||||
d->mimeType = WiiTMDPrivate::mimeTypes[0]; // unofficial
|
||||
d->fileType = FileType::MetadataFile;
|
||||
|
||||
if (!d->file) {
|
||||
// Could not ref() the file handle.
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the ticket. (either v0 or v1, depending on how much was read)
|
||||
d->file->rewind();
|
||||
size_t size = d->file->read(&d->tmdHeader, sizeof(d->tmdHeader));
|
||||
if (size != sizeof(_RVL_TMD_Header)) {
|
||||
// Ticket is too small.
|
||||
d->file.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this ticket is supported.
|
||||
const char *const filename = file->filename();
|
||||
const DetectInfo info = {
|
||||
{0, sizeof(d->tmdHeader), reinterpret_cast<const uint8_t*>(&d->tmdHeader)},
|
||||
FileSystem::file_ext(filename), // ext
|
||||
d->file->size() // szFile
|
||||
};
|
||||
d->isValid = (isRomSupported_static(&info) >= 0);
|
||||
|
||||
if (!d->isValid) {
|
||||
d->file.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a ROM image supported by this class?
|
||||
* @param info DetectInfo containing ROM detection information.
|
||||
* @return Class-specific system ID (>= 0) if supported; -1 if not.
|
||||
*/
|
||||
int WiiTMD::isRomSupported_static(const DetectInfo *info)
|
||||
{
|
||||
assert(info != nullptr);
|
||||
assert(info->header.pData != nullptr);
|
||||
assert(info->header.addr == 0);
|
||||
if (!info || !info->ext || !info->header.pData ||
|
||||
info->header.addr != 0 ||
|
||||
info->header.size < sizeof(RVL_TMD_Header))
|
||||
{
|
||||
// Either no detection information was specified,
|
||||
// or the header is too small.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: File extension must match.
|
||||
bool ok = false;
|
||||
for (const char *const *ext = WiiTMDPrivate::exts;
|
||||
*ext != nullptr; ext++)
|
||||
{
|
||||
if (!strcasecmp(info->ext, *ext)) {
|
||||
// File extension is supported.
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
// File extension doesn't match.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Compare the TMD version to the file size.
|
||||
const RVL_TMD_Header *const tmdHeader = reinterpret_cast<const RVL_TMD_Header*>(info->header.pData);
|
||||
switch (tmdHeader->tmd_format_version) {
|
||||
default:
|
||||
// Unsupported ticket version.
|
||||
return -1;
|
||||
case 0:
|
||||
// TODO: Calculate the actual CMD size.
|
||||
if (info->szFile < static_cast<off64_t>(
|
||||
sizeof(RVL_TMD_Header) +
|
||||
sizeof(RVL_Content_Entry)))
|
||||
{
|
||||
// Incorrect file size.
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// TODO: Calculate the actual CMD size.
|
||||
if (info->szFile < static_cast<off64_t>(
|
||||
sizeof(RVL_TMD_Header) +
|
||||
sizeof(WUP_CMD_GroupHeader) +
|
||||
sizeof(WUP_CMD_GroupEntry) +
|
||||
sizeof(WUP_Content_Entry)))
|
||||
{
|
||||
// Incorrect file size.
|
||||
// TODO: Allow larger tickets?
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate the ticket signature format.
|
||||
switch (be32_to_cpu(tmdHeader->signature_type)) {
|
||||
default:
|
||||
// Unsupported signature format.
|
||||
return -1;
|
||||
case RVL_CERT_SIGTYPE_RSA2048_SHA1:
|
||||
// RSA-2048 with SHA-1 (Wii, DSi)
|
||||
break;
|
||||
case WUP_CERT_SIGTYPE_RSA2048_SHA256:
|
||||
case WUP_CERT_SIGTYPE_RSA2048_SHA256 | WUP_CERT_SIGTYPE_FLAG_DISC:
|
||||
// RSA-2048 with SHA-256 (Wii U, 3DS)
|
||||
// NOTE: Requires TMD format v1 or later.
|
||||
if (tmdHeader->tmd_format_version < 1)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Certificate issuer must start with "Root-".
|
||||
if (memcmp(tmdHeader->signature_issuer, "Root-", 5) != 0) {
|
||||
// Incorrect issuer.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This appears to be a valid Nintendo title metadata.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the system the loaded ROM is designed for.
|
||||
* @param type System name type. (See the SystemName enum.)
|
||||
* @return System name, or nullptr if type is invalid.
|
||||
*/
|
||||
const char *WiiTMD::systemName(unsigned int type) const
|
||||
{
|
||||
RP_D(const WiiTMD);
|
||||
if (!d->isValid || !isSystemNameTypeValid(type))
|
||||
return nullptr;
|
||||
|
||||
// GBA has the same name worldwide, so we can
|
||||
// ignore the region selection.
|
||||
// TODO: Abbreviation might be different... (Japan uses AGB?)
|
||||
static_assert(SYSNAME_TYPE_MASK == 3,
|
||||
"WiiTMD::systemName() array index optimization needs to be updated.");
|
||||
|
||||
// Use the title ID to determine the system.
|
||||
static const char *const sysNames[8][4] = {
|
||||
{"Nintendo Wii", "Wii", "Wii", nullptr}, // Wii IOS
|
||||
{"Nintendo Wii", "Wii", "Wii", nullptr}, // Wii
|
||||
{"GBA NetCard", "NetCard", "NetCard", nullptr}, // GBA NetCard
|
||||
{"Nintendo DSi", "DSi", "DSi", nullptr}, // DSi
|
||||
{"Nintendo 3DS", "3DS", "3DS", nullptr}, // 3DS
|
||||
{"Nintendo Wii U", "Wii U", "Wii U", nullptr}, // Wii U
|
||||
{nullptr, nullptr, nullptr, nullptr}, // unused
|
||||
{"Nintendo Wii U", "Wii U", "Wii U", nullptr}, // Wii U (vWii)
|
||||
};
|
||||
|
||||
const unsigned int sysID = be16_to_cpu(d->tmdHeader.title_id.sysID);
|
||||
return (likely(sysID < ARRAY_SIZE(sysNames)))
|
||||
? sysNames[sysID][type & SYSNAME_TYPE_MASK]
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load field data.
|
||||
* Called by RomData::fields() if the field data hasn't been loaded yet.
|
||||
* @return Number of fields read on success; negative POSIX error code on error.
|
||||
*/
|
||||
int WiiTMD::loadFieldData(void)
|
||||
{
|
||||
RP_D(WiiTMD);
|
||||
if (!d->fields.empty()) {
|
||||
// Field data *has* been loaded...
|
||||
return 0;
|
||||
} else if (!d->file) {
|
||||
// File isn't open.
|
||||
return -EBADF;
|
||||
} else if (!d->isValid) {
|
||||
// TMD isn't valid.
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// TMD header is read in the constructor.
|
||||
const RVL_TMD_Header *const tmdHeader = &d->tmdHeader;
|
||||
d->fields.reserve(4); // Maximum of 4 fields.
|
||||
|
||||
// Title ID
|
||||
char s_title_id[24];
|
||||
snprintf(s_title_id, sizeof(s_title_id), "%08X-%08X",
|
||||
be32_to_cpu(tmdHeader->title_id.hi),
|
||||
be32_to_cpu(tmdHeader->title_id.lo));
|
||||
d->fields.addField_string(C_("Nintendo", "Title ID"), s_title_id, RomFields::STRF_MONOSPACE);
|
||||
|
||||
// Issuer
|
||||
d->fields.addField_string(C_("Nintendo", "Issuer"),
|
||||
latin1_to_utf8(tmdHeader->signature_issuer, sizeof(tmdHeader->signature_issuer)),
|
||||
RomFields::STRF_MONOSPACE | RomFields::STRF_TRIM_END);
|
||||
|
||||
// Title version
|
||||
// TODO: Might be different on 3DS?
|
||||
const unsigned int title_version = be16_to_cpu(tmdHeader->title_version);
|
||||
d->fields.addField_string(C_("Nintendo", "Title Version"),
|
||||
rp_sprintf("%u.%u (v%u)", title_version >> 8, title_version & 0xFF, title_version));
|
||||
|
||||
// OS version (if non-zero)
|
||||
const Nintendo_TitleID_BE_t os_tid = tmdHeader->sys_version;
|
||||
const unsigned int sysID = be16_to_cpu(os_tid.sysID);
|
||||
if (os_tid.id != 0) {
|
||||
// OS display depends on the system ID.
|
||||
char buf[24];
|
||||
buf[0] = '\0';
|
||||
|
||||
switch (sysID) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case NINTENDO_SYSID_IOS: {
|
||||
// Wii (IOS)
|
||||
if (be32_to_cpu(os_tid.hi) != 1)
|
||||
break;
|
||||
|
||||
// IOS slots
|
||||
const uint32_t tid_lo = be32_to_cpu(os_tid.lo);
|
||||
switch (tid_lo) {
|
||||
case 1:
|
||||
strcpy(buf, "boot2");
|
||||
break;
|
||||
case 2:
|
||||
// TODO: Localize this?
|
||||
strcpy(buf, "System Menu");
|
||||
break;
|
||||
case 256:
|
||||
strcpy(buf, "BC");
|
||||
break;
|
||||
case 257:
|
||||
strcpy(buf, "MIOS");
|
||||
break;
|
||||
case 512:
|
||||
strcpy(buf, "BC-NAND");
|
||||
break;
|
||||
case 513:
|
||||
strcpy(buf, "BC-WFS");
|
||||
break;
|
||||
default:
|
||||
if (tid_lo < 256) {
|
||||
snprintf(buf, sizeof(buf), "IOS%u", tid_lo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NINTENDO_SYSID_WUP: {
|
||||
// Wii U (IOSU)
|
||||
// TODO: Add pre-release versions.
|
||||
if (be32_to_cpu(os_tid.hi) != 0x00050010)
|
||||
break;
|
||||
|
||||
const uint32_t tid_lo = be32_to_cpu(os_tid.lo);
|
||||
if ((tid_lo & 0xFFFF3F00) != 0x10000000) {
|
||||
// Not an IOSU title.
|
||||
// tid_lo should be:
|
||||
// - 0x100040xx for NDEBUG
|
||||
// - 0x100080xx for DEBUG
|
||||
break;
|
||||
}
|
||||
|
||||
const unsigned int debug_flag = (tid_lo & 0xC000);
|
||||
if (debug_flag != 0x4000 && debug_flag != 0x8000) {
|
||||
// Incorrect debug flag.
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "OSv%u %s", (tid_lo & 0xFF),
|
||||
(likely(debug_flag == 0x4000)) ? "NDEBUG" : "DEBUG");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(buf[0] == '\0')) {
|
||||
// Print the OS title ID.
|
||||
snprintf(buf, sizeof(buf), "%08X-%08X",
|
||||
be32_to_cpu(os_tid.hi),
|
||||
be32_to_cpu(os_tid.lo));
|
||||
}
|
||||
d->fields.addField_string(C_("RomData", "OS Version"), buf);
|
||||
}
|
||||
|
||||
// Access rights
|
||||
if (sysID == NINTENDO_SYSID_WII || sysID == NINTENDO_SYSID_WUP) {
|
||||
vector<string> *const v_access_rights_hdr = new vector<string>();
|
||||
v_access_rights_hdr->reserve(2);
|
||||
v_access_rights_hdr->emplace_back("AHBPROT");
|
||||
v_access_rights_hdr->emplace_back(C_("Wii", "DVD Video"));
|
||||
d->fields.addField_bitfield(C_("Wii", "Access Rights"),
|
||||
v_access_rights_hdr, 0, be32_to_cpu(tmdHeader->access_rights));
|
||||
}
|
||||
|
||||
// TODO: Region code, if available?
|
||||
|
||||
// Finished reading the field data.
|
||||
return static_cast<int>(d->fields.count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load metadata properties.
|
||||
* Called by RomData::metaData() if the metadata hasn't been loaded yet.
|
||||
* @return Number of metadata properties read on success; negative POSIX error code on error.
|
||||
*/
|
||||
int WiiTMD::loadMetaData(void)
|
||||
{
|
||||
RP_D(WiiTMD);
|
||||
if (d->metaData != nullptr) {
|
||||
// Metadata *has* been loaded...
|
||||
return 0;
|
||||
} else if (!d->file) {
|
||||
// File isn't open.
|
||||
return -EBADF;
|
||||
} else if (!d->isValid) {
|
||||
// TMD isn't valid.
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Create the metadata object.
|
||||
d->metaData = new RomMetaData();
|
||||
d->metaData->reserve(1); // Maximum of 1 metadata property.
|
||||
|
||||
// TMD header is read in the constructor.
|
||||
const RVL_TMD_Header *const tmdHeader = &d->tmdHeader;
|
||||
|
||||
// Title ID (using as Title)
|
||||
char s_title_id[24];
|
||||
snprintf(s_title_id, sizeof(s_title_id), "%08X-%08X",
|
||||
be32_to_cpu(tmdHeader->title_id.hi),
|
||||
be32_to_cpu(tmdHeader->title_id.lo));
|
||||
d->metaData->addMetaData_string(Property::Title, s_title_id);
|
||||
|
||||
// Finished reading the metadata.
|
||||
return static_cast<int>(d->metaData->count());
|
||||
}
|
||||
|
||||
}
|
19
src/libromdata/Console/WiiTMD.hpp
Normal file
19
src/libromdata/Console/WiiTMD.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
/***************************************************************************
|
||||
* ROM Properties Page shell extension. (libromdata) *
|
||||
* WiiTMD.hpp: Nintendo Wii (and Wii U) title metadata reader. *
|
||||
* *
|
||||
* Copyright (c) 2016-2024 by David Korth. *
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later *
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "librpbase/RomData.hpp"
|
||||
|
||||
namespace LibRomData {
|
||||
|
||||
ROMDATA_DECL_BEGIN(WiiTMD)
|
||||
ROMDATA_DECL_METADATA()
|
||||
ROMDATA_DECL_END()
|
||||
|
||||
}
|
@ -159,7 +159,7 @@ ASSERT_STRUCT(RVL_TimeLimit, 2*sizeof(uint32_t));
|
||||
*/
|
||||
#pragma pack(1)
|
||||
typedef struct PACKED _RVL_Ticket {
|
||||
uint32_t signature_type; // [0x000] Always 0x10001 for RSA-2048.
|
||||
uint32_t signature_type; // [0x000] Signature type
|
||||
uint8_t signature[0x100]; // [0x004] Signature
|
||||
uint8_t padding_sig[0x3C]; // [0x104] Padding (always 0)
|
||||
|
||||
@ -244,42 +244,48 @@ typedef struct PACKED _RVL_Ticket_V1 {
|
||||
ASSERT_STRUCT(RVL_Ticket_V1, 0x350);
|
||||
|
||||
/**
|
||||
* Wii TMD header.
|
||||
* Reference: https://wiibrew.org/wiki/Tmd_file_structure
|
||||
* Wii TMD header
|
||||
* References:
|
||||
* - https://wiibrew.org/wiki/Title_metadata
|
||||
* - https://wiiubrew.org/wiki/Title_metadata
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
typedef struct PACKED _RVL_TMD_Header {
|
||||
uint32_t signature_type; // [0x000] Always 0x10001 for RSA-2048.
|
||||
uint8_t signature[0x100]; // [0x004] Signature.
|
||||
uint8_t padding_sig[0x3C]; // [0x104] Padding. (always 0)
|
||||
uint32_t signature_type; // [0x000] Signature type
|
||||
uint8_t signature[0x100]; // [0x004] Signature
|
||||
uint8_t padding_sig[0x3C]; // [0x104] Padding (always 0)
|
||||
|
||||
// The following fields are all covered by the above signature.
|
||||
char signature_issuer[0x40]; // [0x140] Signature issuer.
|
||||
uint8_t version; // [0x180] Version.
|
||||
uint8_t ca_crl_version; // [0x181] CA CRL version.
|
||||
uint8_t signer_crl_version; // [0x182] Signer CRL version.
|
||||
char signature_issuer[0x40]; // [0x140] Signature issuer
|
||||
uint8_t tmd_format_version; // [0x180] TMD format version (v0 for Wii; v1 for Wii U)
|
||||
uint8_t ca_crl_version; // [0x181] CA CRL version
|
||||
uint8_t signer_crl_version; // [0x182] Signer CRL version
|
||||
uint8_t padding1; // [0x183]
|
||||
Nintendo_TitleID_BE_t sys_version; // [0x184] System version. (IOS title ID)
|
||||
Nintendo_TitleID_BE_t title_id; // [0x18C] Title ID.
|
||||
uint32_t title_type; // [0x194] Title type.
|
||||
uint16_t group_id; // [0x198] Group ID.
|
||||
Nintendo_TitleID_BE_t sys_version; // [0x184] System version [IOS(U) title ID]
|
||||
Nintendo_TitleID_BE_t title_id; // [0x18C] Title ID
|
||||
uint32_t title_type; // [0x194] Title type
|
||||
uint16_t group_id; // [0x198] Group ID
|
||||
uint16_t reserved1; // [0x19A]
|
||||
|
||||
// region_code and ratings are NOT valid for discs.
|
||||
// They're only valid for WiiWare.
|
||||
uint16_t region_code; // [0x19C] Region code. (See GCN_Region_Code.)
|
||||
uint8_t ratings[0x10]; // [0x19E] Country-specific age ratings.
|
||||
uint16_t region_code; // [0x19C] Region code (See GCN_Region_Code)
|
||||
uint8_t ratings[0x10]; // [0x19E] Country-specific age ratings
|
||||
uint8_t reserved3[12]; // [0x1AE]
|
||||
|
||||
uint8_t ipc_mask[12]; // [0x1BA] IPC mask.
|
||||
uint8_t ipc_mask[12]; // [0x1BA] IPC mask
|
||||
uint8_t reserved4[18]; // [0x1C6]
|
||||
uint32_t access_rights; // [0x1D8] Access rights. (See RVL_Access_Rights_e.)
|
||||
uint16_t title_version; // [0x1DC] Title version.
|
||||
uint16_t nbr_cont; // [0x1DE] Number of contents.
|
||||
uint16_t boot_index; // [0x1E0] Boot index.
|
||||
uint32_t access_rights; // [0x1D8] Access rights (See RVL_Access_Rights_e)
|
||||
uint16_t title_version; // [0x1DC] Title version
|
||||
uint16_t nbr_cont; // [0x1DE] Number of contents
|
||||
uint16_t boot_index; // [0x1E0] Boot index
|
||||
uint8_t padding2[2]; // [0x1E2]
|
||||
|
||||
// Following this header is a variable-length content table.
|
||||
// Following this header is:
|
||||
// - v0: Content table (length indicated by nbr_cont)
|
||||
// - v1: CMD group header
|
||||
} RVL_TMD_Header;
|
||||
ASSERT_STRUCT(RVL_TMD_Header, 0x1E4);
|
||||
#pragma pack()
|
||||
@ -293,8 +299,10 @@ typedef enum {
|
||||
} RVL_Access_Rights_e;
|
||||
|
||||
/**
|
||||
* Wii content entry. (Stored after the TMD.)
|
||||
* Wii content entry (Stored after the TMD) (v0)
|
||||
* Reference: https://wiibrew.org/wiki/Title_metadata
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
typedef struct PACKED _RVL_Content_Entry {
|
||||
@ -304,7 +312,7 @@ typedef struct PACKED _RVL_Content_Entry {
|
||||
uint64_t size; // [0x008] Size
|
||||
uint8_t sha1_hash[20]; // [0x010] SHA-1 hash of the content (installed) or H3 table (disc).
|
||||
} RVL_Content_Entry;
|
||||
ASSERT_STRUCT(RVL_Content_Entry, 0x24);
|
||||
ASSERT_STRUCT(RVL_Content_Entry, 36);
|
||||
#pragma pack()
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ROM Properties Page shell extension. (libromdata) *
|
||||
* wiiu_structs.h: Nintendo Wii U data structures. *
|
||||
* *
|
||||
* Copyright (c) 2016-2023 by David Korth. *
|
||||
* Copyright (c) 2016-2024 by David Korth. *
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later *
|
||||
***************************************************************************/
|
||||
|
||||
@ -50,6 +50,47 @@ ASSERT_STRUCT(WiiU_DiscHeader, 22);
|
||||
// Secondary Wii U disc magic at 0x10000.
|
||||
#define WIIU_SECONDARY_MAGIC 0xCC549EB9
|
||||
|
||||
/**
|
||||
* Wii U CMD group entry (for v1 TMD)
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
typedef struct _WUP_CMD_GroupEntry {
|
||||
uint16_t offset; // [0x000] Offset of the CMD group
|
||||
uint16_t nbr_cont; // [0x002] Number of CMDs in the group
|
||||
uint8_t sha256_hash[32]; // [0x004] SHA-256 hash of the CMDs in the group
|
||||
} WUP_CMD_GroupEntry;
|
||||
ASSERT_STRUCT(WUP_CMD_GroupEntry, 36);
|
||||
|
||||
/**
|
||||
* Wii U CMD group header (for v1 TMD)
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
typedef struct _WUP_CMD_GroupHeader {
|
||||
uint8_t sha256_hash[32]; // [0x000] SHA-256 hash of CMD groups
|
||||
WUP_CMD_GroupEntry entries[64]; // [0x020] Up to 64 CMD group entries
|
||||
} WUP_CMD_GroupHeader;
|
||||
ASSERT_STRUCT(WUP_CMD_GroupHeader, 2336);
|
||||
#pragma pack()
|
||||
|
||||
/**
|
||||
* Wii U content entry (Stored after the TMD) (v1)
|
||||
* Reference: https://wiibrew.org/wiki/Title_metadata
|
||||
*
|
||||
* All fields are big-endian.
|
||||
*/
|
||||
typedef struct _WUP_Content_Entry {
|
||||
uint32_t content_id; // [0x000] Content ID
|
||||
uint16_t index; // [0x004] Index
|
||||
uint16_t type; // [0x006] Type (see RVL_Content_Type_e)
|
||||
uint64_t size; // [0x008] Size
|
||||
uint8_t sha1_hash[20]; // [0x010] SHA-1 hash of the content (installed) or H3 table (disc).
|
||||
uint8_t unused[12]; // [0x024] Unused. (Maybe it was going to be used for SHA-256?)
|
||||
} WUP_Content_Entry;
|
||||
ASSERT_STRUCT(WUP_Content_Entry, 48);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -53,6 +53,7 @@ using std::vector;
|
||||
#include "Console/SufamiTurbo.hpp"
|
||||
#include "Console/WiiSave.hpp"
|
||||
#include "Console/WiiTicket.hpp"
|
||||
#include "Console/WiiTMD.hpp"
|
||||
#include "Console/WiiU.hpp"
|
||||
#include "Console/WiiWAD.hpp"
|
||||
#include "Console/WiiWIBN.hpp"
|
||||
@ -391,6 +392,7 @@ const RomDataFactoryPrivate::RomDataFns RomDataFactoryPrivate::romDataFns_header
|
||||
GetRomDataFns(SegaSaturn, ATTR_NONE | ATTR_HAS_METADATA | ATTR_SUPPORTS_DEVICES),
|
||||
GetRomDataFns(WiiSave, ATTR_HAS_THUMBNAIL),
|
||||
GetRomDataFns(WiiTicket, ATTR_HAS_METADATA),
|
||||
GetRomDataFns(WiiTMD, ATTR_HAS_METADATA),
|
||||
GetRomDataFns(WiiWAD, ATTR_HAS_THUMBNAIL | ATTR_HAS_METADATA),
|
||||
|
||||
// Handhelds
|
||||
|
@ -36,6 +36,7 @@ application/x-sms-rom # Sega8Bit
|
||||
application/x-gamegear-rom # Sega8Bit
|
||||
application/x-saturn-rom # SegaSaturn
|
||||
application/x-nintendo-ticket # WiiTicket
|
||||
application/x-nintendo-tmd # WiiTMD
|
||||
|
||||
# Handheld
|
||||
application/x-atari-lynx-rom # Lynx
|
||||
|
@ -516,6 +516,13 @@
|
||||
<glob pattern="*.tik"/>
|
||||
</mime-type>
|
||||
|
||||
<!-- WiiTMD -->
|
||||
<mime-type type="application/x-nintendo-tmd">
|
||||
<comment>Nintendo Title Metadata</comment>
|
||||
<generic-icon name="application-pkix-cert"/>
|
||||
<glob pattern="*.tmd"/>
|
||||
</mime-type>
|
||||
|
||||
<!-- WiiU -->
|
||||
<mime-type type="application/x-wii-u-rom">
|
||||
<comment>Wii U disc image</comment>
|
||||
|
Loading…
Reference in New Issue
Block a user