[librvth] RvtH: Move private fields into an actual private class, RvtHPrivate.

Reworked RvtH's inline accessors into non-inline accessors.

Moved the private functions from rvth.cpp to rvth_p.cpp.
This commit is contained in:
David Korth 2025-05-21 19:22:44 -04:00
parent 86a5f70c76
commit 635fd8c89e
11 changed files with 312 additions and 218 deletions

View File

@ -55,6 +55,7 @@ SET(librvth_SRCS
SET(librvth_H
nhcd_structs.h
rvth.hpp
rvth_p.hpp
rvth_time.h
RefFile.hpp
disc_header.hpp

View File

@ -2,14 +2,14 @@
* RVT-H Tool (librvth) *
* disc_header.cpp: Read a GCN/Wii disc header and determine its type. *
* *
* Copyright (c) 2018-2019 by David Korth. *
* Copyright (c) 2018-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
#include "disc_header.hpp"
#include "nhcd_structs.h"
#include "rvth_enums.h"
#include "rvth.hpp" // for RvtH::isBlockEmpty()
#include "rvth_p.hpp" // for RvtH::isBlockEmpty()
#include "RefFile.hpp"
@ -211,7 +211,7 @@ int rvth_disc_header_get(RefFile *f_img, uint32_t lba_start,
// If unknown, check if the entire sector is empty.
if (ret == RVTH_BankType_Unknown) {
if (RvtH::isBlockEmpty(sbuf.u8, sizeof(sbuf.u8))) {
if (RvtHPrivate::isBlockEmpty(sbuf.u8, sizeof(sbuf.u8))) {
// Empty sector.
ret = RVTH_BankType_Empty;
}

View File

@ -7,9 +7,11 @@
***************************************************************************/
#include "rvth.hpp"
#include "ptbl.h"
#include "rvth_p.hpp"
#include "rvth_error.h"
#include "ptbl.h"
#include "byteswap.h"
#include "nhcd_structs.h"
@ -166,7 +168,7 @@ int RvtH::copyToGcm(RvtH *rvth_dest, unsigned int bank_src, RvtH_Progress_Callba
}
// Check if the source bank can be extracted.
const RvtH_BankEntry *const entry_src = &m_entries[bank_src];
const RvtH_BankEntry *const entry_src = &d_ptr->entries[bank_src];
switch (entry_src->type) {
case RVTH_BankType_GCN:
case RVTH_BankType_Wii_SL:
@ -208,12 +210,12 @@ int RvtH::copyToGcm(RvtH *rvth_dest, unsigned int bank_src, RvtH_Progress_Callba
// either truncate it or don't do sparse writes.
// Make this a sparse file.
entry_dest = &rvth_dest->m_entries[0];
ret = rvth_dest->m_file->makeSparse(LBA_TO_BYTES(entry_dest->lba_len));
entry_dest = &rvth_dest->d_ptr->entries[0];
ret = rvth_dest->d_ptr->file->makeSparse(LBA_TO_BYTES(entry_dest->lba_len));
if (ret != 0) {
// Error managing the sparse file.
// TODO: Delete the file?
err = rvth_dest->m_file->lastError();
err = rvth_dest->d_ptr->file->lastError();
if (err == 0) {
err = ENOMEM;
}
@ -289,7 +291,7 @@ int RvtH::copyToGcm(RvtH *rvth_dest, unsigned int bank_src, RvtH_Progress_Callba
// Check for empty 4 KB blocks.
for (unsigned int sprs = 0; sprs < BUF_SIZE; sprs += 4096) {
if (!isBlockEmpty(&buf[sprs], 4096)) {
if (!d_ptr->isBlockEmpty(&buf[sprs], 4096)) {
// 4 KB block is not empty.
lba_nonsparse = lba_count + (sprs / 512);
entry_dest->reader->write(&buf[sprs], lba_nonsparse, 8);
@ -319,7 +321,7 @@ int RvtH::copyToGcm(RvtH *rvth_dest, unsigned int bank_src, RvtH_Progress_Callba
// Check for empty 512-byte blocks.
for (unsigned int sprs = 0; sprs < sz_left; sprs += 512) {
if (!isBlockEmpty(&buf[sprs], 512)) {
if (!d_ptr->isBlockEmpty(&buf[sprs], 512)) {
// 512-byte block is not empty.
lba_nonsparse = lba_count + (sprs / 512);
entry_dest->reader->write(&buf[sprs], lba_nonsparse, 1);
@ -393,7 +395,7 @@ int RvtH::extract(unsigned int bank, const TCHAR *filename,
// handle it as -1.
// Create a standalone disc image.
RvtH_BankEntry *const entry = &m_entries[bank];
RvtH_BankEntry *const entry = &d_ptr->entries[bank];
const bool unenc_to_enc = (entry->type >= RVTH_BankType_Wii_SL &&
entry->crypto_type == RVL_CryptoType_None &&
recrypt_key > RVL_CryptoType_Unknown);
@ -461,7 +463,7 @@ int RvtH::extract(unsigned int bank, const TCHAR *filename,
if (flags & RVTH_EXTRACT_PREPEND_SDK_HEADER) {
// Prepend 32k to the GCM.
size_t size;
Reader *const reader = rvth_dest->m_entries[0].reader;
Reader *const reader = rvth_dest->d_ptr->entries[0].reader;
uint8_t *const sdk_header = static_cast<uint8_t*>(calloc(1, SDK_HEADER_SIZE_BYTES));
if (!sdk_header) {
int ret = -errno;
@ -569,7 +571,7 @@ int RvtH::copyToHDD(RvtH *rvth_dest, unsigned int bank_dest,
}
// Check if the source bank can be imported.
const RvtH_BankEntry *const entry_src = &m_entries[bank_src];
const RvtH_BankEntry *const entry_src = &d_ptr->entries[bank_src];
switch (entry_src->type) {
case RVTH_BankType_GCN:
case RVTH_BankType_Wii_SL:
@ -598,7 +600,7 @@ int RvtH::copyToHDD(RvtH *rvth_dest, unsigned int bank_dest,
// Get the bank count of the destination RVT-H device.
unsigned int bank_count_dest = rvth_dest->bankCount();
// Destination bank entry.
RvtH_BankEntry *const entry_dest = &rvth_dest->m_entries[bank_dest];
RvtH_BankEntry *const entry_dest = &rvth_dest->d_ptr->entries[bank_dest];
// Source image length cannot be larger than a single bank.
RvtH_BankEntry *entry_dest2 = nullptr;
@ -633,7 +635,7 @@ int RvtH::copyToHDD(RvtH *rvth_dest, unsigned int bank_dest,
}
// Check that the second bank is empty or deleted.
entry_dest2 = &rvth_dest->m_entries[bank_dest+1];
entry_dest2 = &rvth_dest->d_ptr->entries[bank_dest+1];
if (entry_dest2->type != RVTH_BankType_Empty &&
!entry_dest2->is_deleted)
{
@ -685,7 +687,7 @@ int RvtH::copyToHDD(RvtH *rvth_dest, unsigned int bank_dest,
}
// Make the destination RVT-H object writable.
ret = rvth_dest->makeWritable();
ret = rvth_dest->d_ptr->makeWritable();
if (ret != 0) {
// Could not make the RVT-H object writable.
int err;
@ -705,7 +707,7 @@ int RvtH::copyToHDD(RvtH *rvth_dest, unsigned int bank_dest,
}
// NOTE: Using the source LBA length, since we might be
// importing a dual-layer Wii image.
entry_dest->reader = Reader::open(rvth_dest->m_file,
entry_dest->reader = Reader::open(rvth_dest->d_ptr->file,
entry_dest->lba_start, entry_src->lba_len);
if (!entry_dest->reader) {
// Cannot create a reader...
@ -822,7 +824,7 @@ int RvtH::copyToHDD(RvtH *rvth_dest, unsigned int bank_dest,
// Update the bank table.
// TODO: Check for errors.
rvth_dest->writeBankEntry(bank_dest);
rvth_dest->d_ptr->writeBankEntry(bank_dest);
// Finished importing the disc image.
return 0;

View File

@ -7,9 +7,11 @@
***************************************************************************/
#include "rvth.hpp"
#include "rvth_p.hpp"
#include "rvth_error.h"
#include "disc_header.hpp"
#include "ptbl.h"
#include "rvth_error.h"
#include "byteswap.h"
#include "nhcd_structs.h"
@ -225,7 +227,7 @@ int RvtH::copyToGcm_doCrypt(RvtH *rvth_dest, unsigned int bank_src,
}
// Check if the source bank can be extracted.
RvtH_BankEntry *const entry_src = &m_entries[bank_src];
RvtH_BankEntry *const entry_src = &d_ptr->entries[bank_src];
switch (entry_src->type) {
case RVTH_BankType_Wii_SL:
case RVTH_BankType_Wii_DL:
@ -307,7 +309,7 @@ int RvtH::copyToGcm_doCrypt(RvtH *rvth_dest, unsigned int bank_src,
// tell the file system what the file's size will be.
// Copy the bank table information.
entry_dest = &rvth_dest->m_entries[0];
entry_dest = &rvth_dest->d_ptr->entries[0];
entry_dest->type = entry_src->type;
entry_dest->region_code = entry_src->region_code;
entry_dest->is_deleted = false;

View File

@ -7,9 +7,11 @@
***************************************************************************/
#include "rvth.hpp"
#include "ptbl.h"
#include "rvth_p.hpp"
#include "rvth_error.h"
#include "ptbl.h"
// For LBA_TO_BYTES()
#include "nhcd_structs.h"
@ -159,7 +161,7 @@ int RvtH::recryptID(unsigned int bank)
}
// Check the bank type.
RvtH_BankEntry *const entry = &m_entries[bank];
RvtH_BankEntry *const entry = &d_ptr->entries[bank];
bool is_wii;
switch (entry->type) {
case RVTH_BankType_GCN:
@ -187,7 +189,7 @@ int RvtH::recryptID(unsigned int bank)
}
// Make the RVT-H object writable.
int ret = this->makeWritable();
int ret = d_ptr->makeWritable();
if (ret != 0) {
// Could not make the RVT-H object writable.
if (ret < 0) {
@ -229,7 +231,7 @@ int RvtH::recryptID(unsigned int bank)
reader->flush();
// Only if this area is empty!
if (isBlockEmpty(&sbuf.u8[0x80], 256)) {
if (d_ptr->isBlockEmpty(&sbuf.u8[0x80], 256)) {
rvth_create_id(&sbuf.u8[0x80], 256, &gcn, NULL);
errno = 0;
lba_size = reader->write(&sbuf.u8, BYTES_TO_LBA(0x400), 1);
@ -275,7 +277,7 @@ int RvtH::recryptID(unsigned int bank)
}
// Only if this area is empty!
if (!isBlockEmpty(&id_buf[256], 256))
if (!d_ptr->isBlockEmpty(&id_buf[256], 256))
continue;
// Write the identifier.
@ -364,7 +366,7 @@ int RvtH::recryptWiiPartitions(unsigned int bank,
}
// Check the bank type.
RvtH_BankEntry *const entry = &m_entries[bank];
RvtH_BankEntry *const entry = &d_ptr->entries[bank];
switch (entry->type) {
case RVTH_BankType_Wii_SL:
case RVTH_BankType_Wii_DL:
@ -422,7 +424,7 @@ int RvtH::recryptWiiPartitions(unsigned int bank,
// since we're doing that for each partition individually.
// Make the RVT-H object writable.
ret = this->makeWritable();
ret = d_ptr->makeWritable();
if (ret != 0) {
// Could not make the RVT-H object writable.
int err;
@ -646,7 +648,7 @@ int RvtH::recryptWiiPartitions(unsigned int bank,
// Write the identifier.
// (Only if this area is empty!)
if (isBlockEmpty(&hdr_new.data[sizeof(hdr_new.data)-256], 256)) {
if (d_ptr->isBlockEmpty(&hdr_new.data[sizeof(hdr_new.data)-256], 256)) {
char ptid_buf[24];
snprintf(ptid_buf, sizeof(ptid_buf), "%up%u -> %up%u",
pte->vg, pte->pt_orig,
@ -724,7 +726,7 @@ int RvtH::recryptWiiPartitions(unsigned int bank,
// If this is an HDD, write the bank table entry.
if (isHDD()) {
// TODO: Check for errors.
this->writeBankEntry(bank);
d_ptr->writeBankEntry(bank);
}
// Finished processing the disc image.

View File

@ -7,12 +7,13 @@
***************************************************************************/
#include "rvth.hpp"
#include "rvth_p.hpp"
#include "rvth_error.h"
#include "nhcd_structs.h"
#include "disc_header.hpp"
#include "ptbl.h"
#include "bank_init.h"
#include "rvth_error.h"
#include "reader/Reader.hpp"
#include "libwiicrypto/byteswap.h"
@ -105,14 +106,14 @@ int RvtH::openGcm(RefFile *f_img)
}
// Allocate memory for a single RvtH_BankEntry object.
m_entries.resize(1);
m_imageType = reader->type();
d_ptr->entries.resize(1);
d_ptr->imageType = reader->type();
// Initialize the bank entry.
// NOTE: Not using rvth_init_BankEntry() here.
m_file = f_img->ref();
m_NHCD_status = NHCD_STATUS_MISSING;
entry = m_entries.data();
d_ptr->file = f_img->ref();
d_ptr->nhcdStatus = NHCD_STATUS_MISSING;
entry = d_ptr->entries.data();
entry->lba_start = reader->lba_start();
entry->lba_len = reader->lba_len();
entry->type = type;
@ -151,9 +152,9 @@ int RvtH::openGcm(RefFile *f_img)
fail:
// Failed to open the disc image.
delete reader;
if (m_file) {
m_file->unref();
m_file = nullptr;
if (d_ptr->file) {
d_ptr->file->unref();
d_ptr->file = nullptr;
}
if (err != 0) {
errno = err;
@ -258,9 +259,9 @@ int RvtH::openHDD(RefFile *f_img)
size_t size;
// Check the bank table header.
m_nhcdHeader.reset(new NHCD_BankTable_Header);
d_ptr->nhcdHeader.reset(new NHCD_BankTable_Header);
size = f_img->seekoAndRead(LBA_TO_BYTES(NHCD_BANKTABLE_ADDRESS_LBA), SEEK_SET,
m_nhcdHeader.get(), 1, sizeof(NHCD_BankTable_Header));
d_ptr->nhcdHeader.get(), 1, sizeof(NHCD_BankTable_Header));
if (size != sizeof(NHCD_BankTable_Header)) {
// Short read.
err = errno;
@ -268,19 +269,19 @@ int RvtH::openHDD(RefFile *f_img)
err = EIO;
}
ret = -err;
m_nhcdHeader.reset();
d_ptr->nhcdHeader.reset();
goto fail;
}
// Determine the device type.
m_imageType = (f_img->isDevice()
d_ptr->imageType = (f_img->isDevice()
? RVTH_ImageType_HDD_Reader
: RVTH_ImageType_HDD_Image);
// Check the magic number.
if (m_nhcdHeader->magic == be32_to_cpu(NHCD_BANKTABLE_MAGIC)) {
if (d_ptr->nhcdHeader->magic == be32_to_cpu(NHCD_BANKTABLE_MAGIC)) {
// Magic number is correct.
m_NHCD_status = NHCD_STATUS_OK;
d_ptr->nhcdStatus = NHCD_STATUS_OK;
} else {
// Incorrect magic number.
// We'll continue with a default bank table.
@ -297,19 +298,19 @@ int RvtH::openHDD(RefFile *f_img)
}
if (hasGPT) {
m_NHCD_status = NHCD_STATUS_HAS_GPT;
d_ptr->nhcdStatus = NHCD_STATUS_HAS_GPT;
} else if (hasMBR) {
m_NHCD_status = NHCD_STATUS_HAS_MBR;
d_ptr->nhcdStatus = NHCD_STATUS_HAS_MBR;
} else {
m_NHCD_status = NHCD_STATUS_MISSING;
d_ptr->nhcdStatus = NHCD_STATUS_MISSING;
}
// Assuming a default 8-bank system.
static const unsigned int bankCount_default = 8;
m_entries.resize(bankCount_default);
d_ptr->entries.resize(bankCount_default);
m_file = f_img->ref();
rvth_entry = m_entries.data();
d_ptr->file = f_img->ref();
rvth_entry = d_ptr->entries.data();
lba_start = NHCD_BANK_START_LBA(0, 8);
for (unsigned int i = 0; i < bankCount_default; i++, rvth_entry++, lba_start += NHCD_BANK_SIZE_LBA) {
// Use "Empty" so we can try to detect the actual bank type.
@ -323,7 +324,7 @@ int RvtH::openHDD(RefFile *f_img)
}
// Get the bank count.
bankCount = be32_to_cpu(m_nhcdHeader->bank_count);
bankCount = be32_to_cpu(d_ptr->nhcdHeader->bank_count);
if (bankCount < 8 || bankCount > 32) {
// Bank count is either too small or too large.
// RVT-H systems are set to 8 banks at the factory,
@ -339,10 +340,10 @@ int RvtH::openHDD(RefFile *f_img)
}
// Allocate memory for the RvtH_BankEntry objects.
m_entries.resize(bankCount);
d_ptr->entries.resize(bankCount);
m_file = f_img->ref();
rvth_entry = m_entries.data();
d_ptr->file = f_img->ref();
rvth_entry = d_ptr->entries.data();
// FIXME: Why cast to uint32_t?
addr = (uint32_t)(LBA_TO_BYTES(NHCD_BANKTABLE_ADDRESS_LBA) + NHCD_BLOCK_SIZE);
for (unsigned int i = 0; i < bankCount; i++, rvth_entry++, addr += 512) {
@ -426,9 +427,9 @@ int RvtH::openHDD(RefFile *f_img)
fail:
// Failed to open the HDD image.
if (m_file) {
m_file->unref();
m_file = nullptr;
if (d_ptr->file) {
d_ptr->file->unref();
d_ptr->file = nullptr;
}
if (err != 0) {
errno = err;
@ -442,9 +443,7 @@ fail:
* @param pErr [out,opt] Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
RvtH::RvtH(const TCHAR *filename, int *pErr)
: m_file(nullptr)
, m_imageType(RVTH_ImageType_Unknown)
, m_NHCD_status(NHCD_STATUS_UNKNOWN)
: d_ptr(new RvtHPrivate(this))
{
// Open the disk image.
RefFile *const f_img = new RefFile(filename);
@ -496,42 +495,75 @@ RvtH::~RvtH()
// Close all bank entry files.
// RefFile has a reference count, so we have to clear the count.
for (unsigned int i = 0; i < bankCount(); i++) {
delete m_entries[i].reader;
free(m_entries[i].ptbl);
delete d_ptr->entries[i].reader;
free(d_ptr->entries[i].ptbl);
}
// Clear the main file reference.
if (m_file) {
m_file->unref();
if (d_ptr->file) {
d_ptr->file->unref();
}
}
/**
* Is the file open?
* @return True if open; false if not.
*/
bool RvtH::isOpen(void) const
{
return (d_ptr->file != nullptr);
}
/**
* Get the number of banks in the RVT-H disk image.
* @return Number of banks
*/
unsigned int RvtH::bankCount(void) const
{
return d_ptr->bankCount();
}
/**
* Is this RVT-H object an RVT-H Reader / HDD image, or a standalone disc image?
* @param rvth RVT-H object.
* @param rvth RVT-H object
* @return True if the RVT-H object is an RVT-H Reader / HDD image; false if it's a standalone disc image.
*/
bool RvtH::isHDD(void) const
{
switch (m_imageType) {
case RVTH_ImageType_HDD_Reader:
case RVTH_ImageType_HDD_Image:
return true;
return d_ptr->isHDD();
}
default:
break;
}
/**
* Get the RVT-H image type.
* @param rvth RVT-H object
* @return RVT-H image type
*/
RvtH_ImageType_e RvtH::imageType(void) const
{
return d_ptr->imageType;
}
return false;
/**
* Get the NHCD table status.
*
* For optical disc images, this is always NHCD_STATUS_MISSING.
* For HDD images, this should be NHCD_STATUS_OK unless the
* NHCD table was wiped or a PC-partitioned disk was selected.
*
* @return NHCD table status
*/
NHCD_Status_e RvtH::nhcd_status(void) const
{
return d_ptr->nhcdStatus;
}
/**
* Get the NHCD Table Header.
* @return NHCD Bank Table Header.
* @return NHCD Bank Table Header
*/
NHCD_BankTable_Header *RvtH::nhcd_header(void) const
{
return m_nhcdHeader.get();
return d_ptr->nhcdHeader.get();
}
/**
@ -550,5 +582,5 @@ const RvtH_BankEntry *RvtH::bankEntry(unsigned int bank, int *pErr) const
return nullptr;
}
return &m_entries[bank];
return &d_ptr->entries[bank];
}

View File

@ -183,12 +183,9 @@ typedef bool (*RvtH_Verify_Progress_Callback)(const RvtH_Verify_Progress_State *
#ifdef __cplusplus
// C++ STL classes
#include <memory>
#include <vector>
/** Main class **/
class RvtHPrivate;
class RvtH {
public:
/**
@ -217,6 +214,10 @@ public:
~RvtH();
private:
RvtHPrivate *const d_ptr;
DISABLE_COPY(RvtH);
private:
/** Constructor functions (rvth.cpp) **/
@ -244,75 +245,33 @@ private:
int openHDD(RefFile *f_img);
public:
/** General utility functions **/
// TODO: Move out of RvtH?
/** Accessors **/
/**
* Check if a block is empty.
* @param block Block.
* @param size Block size. (Must be a multiple of 64 bytes.)
* @return True if the block is all zeroes; false if not.
*/
static bool isBlockEmpty(const uint8_t *block, unsigned int size);
private:
/** Private functions (rvth_p.cpp) **/
/**
* Make the RVT-H object writable.
* @return Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
int makeWritable(void);
/**
* Write a bank table entry to disk.
* @param bank [in] Bank number. (0-7)
* @param pTimestamp [out,opt] Timestamp written to the bank entry.
* @return Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
int writeBankEntry(unsigned int bank, time_t *pTimestamp = nullptr);
private:
DISABLE_COPY(RvtH)
public:
/**
* Is the file open?
* @return True if open; false if not.
*/
inline bool isOpen(void) const
{
return (m_file != nullptr);
}
public:
/** Accessors **/
bool isOpen(void) const;
/**
* Get the number of banks in the RVT-H disk image.
* @return Number of banks.
* @return Number of banks
*/
inline unsigned int bankCount(void) const
{
return static_cast<unsigned int>(m_entries.size());
}
unsigned int bankCount(void) const;
/**
* Is this RVT-H object an HDD image or a standalone disc image?
* @param rvth RVT-H object.
* @param rvth RVT-H object
* @return True if the RVT-H object is an HDD image; false if it's a standalone disc image.
*/
bool isHDD(void) const;
/**
* Get the RVT-H image type.
* @param rvth RVT-H object.
* @return RVT-H image type.
* @param rvth RVT-H object
* @return RVT-H image type
*/
inline RvtH_ImageType_e imageType(void) const
{
return m_imageType;
}
RvtH_ImageType_e imageType(void) const;
/**
* Get the NHCD table status.
@ -321,12 +280,9 @@ public:
* For HDD images, this should be NHCD_STATUS_OK unless the
* NHCD table was wiped or a PC-partitioned disk was selected.
*
* @return NHCD table status.
* @return NHCD table status
*/
inline NHCD_Status_e nhcd_status(void) const
{
return m_NHCD_status;
}
NHCD_Status_e nhcd_status(void) const;
/**
* Get the NHCD Table Header.
@ -505,25 +461,6 @@ public:
unsigned int error_count[5] = nullptr,
RvtH_Verify_Progress_Callback callback = nullptr,
void *userdata = nullptr);
private:
// Reference-counted FILE*
RefFile *m_file;
// Image type
RvtH_ImageType_e m_imageType;
// NHCD header status
NHCD_Status_e m_NHCD_status;
// NHCD bank table header
// NOTE: This will be nullptr for e.g. GCM disc images.
std::unique_ptr<NHCD_BankTable_Header> m_nhcdHeader;
// BankEntry objects
// - RVT-H system or disk image: 8 (usually)
// - Standalone disc image: 1
std::vector<RvtH_BankEntry> m_entries;
};
#endif /* __cplusplus */

View File

@ -1,16 +1,17 @@
/***************************************************************************
* RVT-H Tool (librvth) *
* rvth_p.cpp: RVT-H image handler. (PRIVATE FUNCTIONS) *
* rvth_p.cpp: RVT-H image handler. (PRIVATE CLASS) *
* *
* Copyright (c) 2018-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
#include "rvth.hpp"
#include "rvth_p.hpp"
#include "rvth_error.h"
#include "RefFile.hpp"
#include "rvth_time.h"
#include "rvth_error.h"
#include "byteswap.h"
#include "nhcd_structs.h"
@ -20,44 +21,20 @@
#include <cerrno>
#include <cstring>
/**
* Make the RVT-H object writable.
* @return Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
int RvtH::makeWritable(void)
{
if (m_file->isWritable()) {
// RVT-H is already writable.
return 0;
}
// TODO: Allow making a disc image file writable.
// (Single bank)
// Make sure this is a device file.
if (!m_file->isDevice()) {
// This is not a device file.
// Cannot make it writable.
return RVTH_ERROR_NOT_A_DEVICE;
}
// If we're using a fake NHCD table, we can't write to it.
if (m_NHCD_status != NHCD_STATUS_OK) {
// Fake NHCD table is in use.
return RVTH_ERROR_NHCD_TABLE_MAGIC;
}
// Make this writable.
return m_file->makeWritable();
}
RvtHPrivate::RvtHPrivate(RvtH *q)
: q_ptr(q)
, file(nullptr)
, imageType(RVTH_ImageType_Unknown)
, nhcdStatus(NHCD_STATUS_UNKNOWN)
{ }
/**
* Check if a block is empty.
* @param block Block.
* @param size Block size. (Must be a multiple of 64 bytes.)
* @param block Block
* @param size Block size (Must be a multiple of 64 bytes.)
* @return True if the block is all zeroes; false if not.
*/
bool RvtH::isBlockEmpty(const uint8_t *block, unsigned int size)
bool RvtHPrivate::isBlockEmpty(const uint8_t *block, unsigned int size)
{
// Process the block using 64-bit pointers.
const uint64_t *block64 = (const uint64_t*)block;
@ -82,13 +59,44 @@ bool RvtH::isBlockEmpty(const uint8_t *block, unsigned int size)
return true;
}
/**
* Make the RVT-H object writable.
* @return Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
int RvtHPrivate::makeWritable(void)
{
if (file->isWritable()) {
// RVT-H is already writable.
return 0;
}
// TODO: Allow making a disc image file writable.
// (Single bank)
// Make sure this is a device file.
if (!file->isDevice()) {
// This is not a device file.
// Cannot make it writable.
return RVTH_ERROR_NOT_A_DEVICE;
}
// If we're using a fake NHCD table, we can't write to it.
if (nhcdStatus != NHCD_STATUS_OK) {
// Fake NHCD table is in use.
return RVTH_ERROR_NHCD_TABLE_MAGIC;
}
// Make this writable.
return file->makeWritable();
}
/**
* Write a bank table entry to disk.
* @param bank [in] Bank number. (0-7)
* @param pTimestamp [out,opt] Timestamp written to the bank entry.
* @return Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
int RvtH::writeBankEntry(unsigned int bank, time_t *pTimestamp)
int RvtHPrivate::writeBankEntry(unsigned int bank, time_t *pTimestamp)
{
if (!isHDD()) {
// Standalone disc image. No bank table.
@ -113,7 +121,7 @@ int RvtH::writeBankEntry(unsigned int bank, time_t *pTimestamp)
// If the bank entry is deleted, then it should be
// all zeroes, so skip all of this.
RvtH_BankEntry *const rvth_entry = &m_entries[bank];
RvtH_BankEntry *const rvth_entry = &entries[bank];
if (!rvth_entry->is_deleted) {
// Bank entry is not deleted.
// Construct the NHCD bank entry.
@ -165,7 +173,7 @@ int RvtH::writeBankEntry(unsigned int bank, time_t *pTimestamp)
}
// Write the bank entry.
ret = m_file->seeko(LBA_TO_BYTES(NHCD_BANKTABLE_ADDRESS_LBA + bank+1), SEEK_SET);
ret = file->seeko(LBA_TO_BYTES(NHCD_BANKTABLE_ADDRESS_LBA + bank+1), SEEK_SET);
if (ret != 0) {
// Seek error.
if (errno == 0) {
@ -173,7 +181,7 @@ int RvtH::writeBankEntry(unsigned int bank, time_t *pTimestamp)
}
return -errno;
}
size_t size = m_file->write(&nhcd_entry, 1, sizeof(nhcd_entry));
size_t size = file->write(&nhcd_entry, 1, sizeof(nhcd_entry));
if (size != sizeof(nhcd_entry)) {
// Write error.
if (errno == 0) {

109
src/librvth/rvth_p.hpp Normal file
View File

@ -0,0 +1,109 @@
/***************************************************************************
* RVT-H Tool (librvth) *
* rvth_p.cpp: RVT-H image handler. (PRIVATE CLASS) *
* *
* Copyright (c) 2018-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
#pragma once
#include "rvth.hpp"
// Enums
#include "rvth_enums.h"
#include "nhcd_structs.h"
// C++ STL classes
#include <memory>
#include <vector>
class RvtH;
class RvtHPrivate
{
public:
explicit RvtHPrivate(RvtH *q);
~RvtHPrivate() = default;
private:
RvtH *const q_ptr;
DISABLE_COPY(RvtHPrivate)
public:
/** General utility functions **/
/**
* Check if a block is empty.
* @param block Block
* @param size Block size (Must be a multiple of 64 bytes.)
* @return True if the block is all zeroes; false if not.
*/
static bool isBlockEmpty(const uint8_t *block, unsigned int size);
public:
/** Accessors **/
/**
* Get the number of banks in the RVT-H disk image.
* @return Number of banks.
*/
inline unsigned int bankCount(void) const
{
return static_cast<unsigned int>(entries.size());
}
/**
* Is this RVT-H object an RVT-H Reader / HDD image, or a standalone disc image?
* @param rvth RVT-H object
* @return True if the RVT-H object is an RVT-H Reader / HDD image; false if it's a standalone disc image.
*/
inline bool isHDD(void) const
{
switch (imageType) {
case RVTH_ImageType_HDD_Reader:
case RVTH_ImageType_HDD_Image:
return true;
default:
break;
}
return false;
}
public:
/** Private functions **/
/**
* Make the RVT-H object writable.
* @return Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
int makeWritable(void);
/**
* Write a bank table entry to disk.
* @param bank [in] Bank number. (0-7)
* @param pTimestamp [out,opt] Timestamp written to the bank entry.
* @return Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
int writeBankEntry(unsigned int bank, time_t *pTimestamp = nullptr);
public:
// Reference-counted FILE*
RefFile *file;
// Image type
RvtH_ImageType_e imageType;
// NHCD header status
NHCD_Status_e nhcdStatus;
// NHCD bank table header
// NOTE: This will be nullptr for e.g. GCM disc images.
std::unique_ptr<NHCD_BankTable_Header> nhcdHeader;
// BankEntry objects
// - RVT-H system or disk image: 8 (usually)
// - Standalone disc image: 1
std::vector<RvtH_BankEntry> entries;
};

View File

@ -2,14 +2,16 @@
* RVT-H Tool (librvth) *
* verify.cpp: RVT-H verification functions. *
* *
* Copyright (c) 2018-2024 by David Korth. *
* Copyright (c) 2018-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
#include "rvth.hpp"
#include "ptbl.h"
#include "rvth_p.hpp"
#include "rvth_error.h"
#include "ptbl.h"
// For LBA_TO_BYTES()
#include "nhcd_structs.h"
@ -131,7 +133,7 @@ int RvtH::verifyWiiPartitions(unsigned int bank,
}
// Make sure this is a Wii disc.
RvtH_BankEntry *const entry = &m_entries[bank];
RvtH_BankEntry *const entry = &d_ptr->entries[bank];
switch (entry->type) {
case RVTH_BankType_Wii_SL:
case RVTH_BankType_Wii_DL:

View File

@ -7,6 +7,7 @@
***************************************************************************/
#include "rvth.hpp"
#include "rvth_p.hpp"
#include "rvth_error.h"
#include "byteswap.h"
@ -35,9 +36,7 @@
* @param pErr [out,opt] Error code. (If negative, POSIX error; otherwise, see RvtH_Errors.)
*/
RvtH::RvtH(const TCHAR *filename, uint32_t lba_len, int *pErr)
: m_file(nullptr)
, m_imageType(RVTH_ImageType_Unknown)
, m_NHCD_status(NHCD_STATUS_UNKNOWN)
: d_ptr(new RvtHPrivate(this))
{
RvtH_BankEntry *entry;
@ -53,14 +52,14 @@ RvtH::RvtH(const TCHAR *filename, uint32_t lba_len, int *pErr)
errno = 0;
// Allocate memory for a single RvtH_BankEntry object.
m_entries.resize(1);
m_imageType = RVTH_ImageType_GCM;
d_ptr->entries.resize(1);
d_ptr->imageType = RVTH_ImageType_GCM;
// Attempt to create the file.
m_file = new RefFile(filename, true);
if (!m_file->isOpen()) {
d_ptr->file = new RefFile(filename, true);
if (!d_ptr->file->isOpen()) {
// Error creating the file.
err = m_file->lastError();
err = d_ptr->file->lastError();
if (err == 0) {
err = EIO;
}
@ -69,7 +68,7 @@ RvtH::RvtH(const TCHAR *filename, uint32_t lba_len, int *pErr)
// Initialize the bank entry.
// NOTE: Not using rvth_init_BankEntry() here.
entry = &m_entries[0];
entry = &d_ptr->entries[0];
entry->lba_start = 0;
entry->lba_len = lba_len;
entry->type = RVTH_BankType_Empty;
@ -80,7 +79,7 @@ RvtH::RvtH(const TCHAR *filename, uint32_t lba_len, int *pErr)
entry->timestamp = time(nullptr);
// Initialize the disc image reader.
entry->reader = Reader::open(m_file, entry->lba_start, entry->lba_len);
entry->reader = Reader::open(d_ptr->file, entry->lba_start, entry->lba_len);
if (!entry->reader) {
// Error creating the disc image reader.
err = errno;
@ -96,9 +95,9 @@ RvtH::RvtH(const TCHAR *filename, uint32_t lba_len, int *pErr)
fail:
// Failed to create a GCM file.
// TODO: Delete it in case it was partially created?
if (m_file) {
m_file->unref();
m_file = nullptr;
if (d_ptr->file) {
d_ptr->file->unref();
d_ptr->file = nullptr;
}
if (pErr) {
*pErr = -err;
@ -124,14 +123,14 @@ int RvtH::deleteBank(unsigned int bank)
}
// Make the RVT-H object writable.
int ret = this->makeWritable();
int ret = d_ptr->makeWritable();
if (ret != 0) {
// Could not make the RVT-H object writable.
return ret;
}
// Is the bank deleted?
RvtH_BankEntry *const rvth_entry = &m_entries[bank];
RvtH_BankEntry *const rvth_entry = &d_ptr->entries[bank];
if (rvth_entry->is_deleted) {
// Bank is already deleted.
return RVTH_ERROR_BANK_IS_DELETED;
@ -163,8 +162,8 @@ int RvtH::deleteBank(unsigned int bank)
// Delete the bank and write the entry.
rvth_entry->is_deleted = true;
rvth_entry->timestamp = -1;
ret = this->writeBankEntry(bank);
m_file->flush();
ret = d_ptr->writeBankEntry(bank);
d_ptr->file->flush();
if (ret != 0) {
// Error deleting the bank...
rvth_entry->is_deleted = false;
@ -190,14 +189,14 @@ int RvtH::undeleteBank(unsigned int bank)
}
// Make the RVT-H object writable.
int ret = this->makeWritable();
int ret = d_ptr->makeWritable();
if (ret != 0) {
// Could not make the RVT-H object writable.
return ret;
}
// Is the bank deleted?
RvtH_BankEntry *const rvth_entry = &m_entries[bank];
RvtH_BankEntry *const rvth_entry = &d_ptr->entries[bank];
if (!rvth_entry->is_deleted) {
// Bank is not deleted.
return RVTH_ERROR_BANK_NOT_DELETED;
@ -242,8 +241,8 @@ int RvtH::undeleteBank(unsigned int bank)
// Undelete the bank and write the entry.
rvth_entry->is_deleted = false;
ret = this->writeBankEntry(bank, &rvth_entry->timestamp);
m_file->flush();
ret = d_ptr->writeBankEntry(bank, &rvth_entry->timestamp);
d_ptr->file->flush();
if (ret != 0) {
// Error undeleting the bank...
rvth_entry->is_deleted = true;