Add WIIU reader abstraction: implement WUD/WUX file handling and reading logic

This commit is contained in:
2026-03-16 11:27:50 +00:00
parent 8112899246
commit f8cf406709
2 changed files with 279 additions and 0 deletions

185
tool/wiiu_reader.c Normal file
View File

@@ -0,0 +1,185 @@
/*
* This file is part of the Aaru Data Preservation Suite.
* Copyright (c) 2019-2026 Natalia Portillo.
*
* 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 3 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/>.
*
* WUD/WUX disc reader abstraction for aaruformattool.
*/
#include "wiiu_reader.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* @brief Try to initialize WUX format from an open file.
* @return 0 on success (file is WUX), -1 if not WUX.
*/
static int wux_init(WiiuReader *reader)
{
WuxHeader hdr;
if(fseek(reader->fp, 0, SEEK_SET) != 0) return -1;
if(fread(&hdr, 1, sizeof(hdr), reader->fp) != sizeof(hdr)) return -1;
if(hdr.magic != WUX_MAGIC) return -1;
if(hdr.sector_size == 0 || hdr.sector_size != WIIU_SECTOR_SIZE)
{
fprintf(stderr, "Error: WUX sector size 0x%X is not 0x%X\n", hdr.sector_size, WIIU_SECTOR_SIZE);
return -1;
}
reader->is_wux = 1;
reader->disc_size = hdr.uncompressed_size;
reader->wux_sector_count = (uint32_t)(reader->disc_size / WIIU_SECTOR_SIZE);
/* Read index table (starts at offset 0x20) */
size_t idx_bytes = (size_t)reader->wux_sector_count * sizeof(uint32_t);
reader->wux_index = (uint32_t *)malloc(idx_bytes);
if(reader->wux_index == NULL)
{
fprintf(stderr, "Error: cannot allocate WUX index (%zu bytes)\n", idx_bytes);
return -1;
}
if(fseek(reader->fp, 0x20, SEEK_SET) != 0)
{
free(reader->wux_index);
reader->wux_index = NULL;
return -1;
}
if(fread(reader->wux_index, 1, idx_bytes, reader->fp) != idx_bytes)
{
fprintf(stderr, "Error: cannot read WUX index table\n");
free(reader->wux_index);
reader->wux_index = NULL;
return -1;
}
/* Data starts at next sector-aligned offset after header + index table */
uint64_t raw_data_off = 0x20 + idx_bytes;
reader->wux_data_offset = (raw_data_off + WIIU_SECTOR_SIZE - 1) & ~((uint64_t)WIIU_SECTOR_SIZE - 1);
return 0;
}
int wiiu_reader_open(const char *path, WiiuReader *reader)
{
memset(reader, 0, sizeof(*reader));
reader->fp = fopen(path, "rb");
if(reader->fp == NULL)
{
fprintf(stderr, "Error: cannot open %s\n", path);
return -1;
}
/* Try WUX first */
if(wux_init(reader) == 0) return 0;
/* Fallback: raw WUD — get file size */
reader->is_wux = 0;
if(fseek(reader->fp, 0, SEEK_END) != 0)
{
fprintf(stderr, "Error: cannot determine file size for %s\n", path);
fclose(reader->fp);
reader->fp = NULL;
return -1;
}
long file_size = ftell(reader->fp);
if(file_size <= 0)
{
fprintf(stderr, "Error: cannot determine file size for %s\n", path);
fclose(reader->fp);
reader->fp = NULL;
return -1;
}
reader->disc_size = (uint64_t)file_size;
return 0;
}
void wiiu_reader_close(WiiuReader *reader)
{
if(reader->wux_index != NULL)
{
free(reader->wux_index);
reader->wux_index = NULL;
}
if(reader->fp != NULL)
{
fclose(reader->fp);
reader->fp = NULL;
}
}
int64_t wiiu_reader_read_at(WiiuReader *reader, void *buf, size_t count, uint64_t offset)
{
if(reader == NULL || reader->fp == NULL || buf == NULL) return -1;
if(!reader->is_wux)
{
/* Raw WUD: direct seek + read */
if(fseek(reader->fp, (long)offset, SEEK_SET) != 0) return -1;
size_t n = fread(buf, 1, count, reader->fp);
return n > 0 ? (int64_t)n : -1;
}
/* WUX: translate logical sectors through index table */
uint8_t *out = (uint8_t *)buf;
size_t done = 0;
while(done < count)
{
uint64_t cur_off = offset + done;
if(cur_off >= reader->disc_size) break;
uint32_t logical_sector = (uint32_t)(cur_off / WIIU_SECTOR_SIZE);
uint32_t sector_offset = (uint32_t)(cur_off % WIIU_SECTOR_SIZE);
if(logical_sector >= reader->wux_sector_count) break;
uint32_t physical_sector = reader->wux_index[logical_sector];
uint64_t file_offset = reader->wux_data_offset + (uint64_t)physical_sector * WIIU_SECTOR_SIZE + sector_offset;
size_t chunk = WIIU_SECTOR_SIZE - sector_offset;
if(chunk > count - done) chunk = count - done;
if(fseek(reader->fp, (long)file_offset, SEEK_SET) != 0) break;
size_t n = fread(out + done, 1, chunk, reader->fp);
if(n == 0) break;
done += n;
}
return done > 0 ? (int64_t)done : -1;
}

94
tool/wiiu_reader.h Normal file
View File

@@ -0,0 +1,94 @@
/*
* This file is part of the Aaru Data Preservation Suite.
* Copyright (c) 2019-2026 Natalia Portillo.
*
* 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 3 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/>.
*
* WUD/WUX disc reader abstraction for aaruformattool.
*/
#ifndef AARUFORMATTOOL_WIIU_READER_H
#define AARUFORMATTOOL_WIIU_READER_H
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define WIIU_SECTOR_SIZE 0x8000 /**< Wii U physical sector size (32 KiB). */
#define WUX_MAGIC 0x30585557U /**< "WUX0" as little-endian uint32. */
/** WUX file header (32 bytes). */
typedef struct WuxHeader
{
uint32_t magic; /**< "WUX0" (0x30585557 LE). */
uint32_t reserved; /**< Reserved / version. */
uint32_t sector_size; /**< Sector size (must be 0x8000). */
uint32_t reserved2; /**< Must be 0. */
uint64_t uncompressed_size; /**< Original disc size in bytes. */
uint64_t reserved3; /**< Must be 0. */
} WuxHeader;
/** Reader abstraction that handles both raw WUD and compressed WUX. */
typedef struct WiiuReader
{
FILE *fp; /**< File handle. */
int is_wux; /**< 1 if WUX, 0 if raw WUD. */
uint64_t disc_size; /**< Uncompressed disc size in bytes. */
/* WUX-specific */
uint32_t *wux_index; /**< Sector index table (NULL if WUD). */
uint64_t wux_data_offset; /**< File offset where WUX data sectors start. */
uint32_t wux_sector_count; /**< Number of logical sectors in the disc. */
} WiiuReader;
/**
* @brief Open a WUD or WUX disc image.
*
* Detects the format automatically (WUX if magic matches, else raw WUD).
*
* @param path File path to the disc image.
* @param reader Output: reader structure to populate.
* @return 0 on success, -1 on error.
*/
int wiiu_reader_open(const char *path, WiiuReader *reader);
/**
* @brief Close the disc reader and free associated resources.
*
* @param reader Reader to close.
*/
void wiiu_reader_close(WiiuReader *reader);
/**
* @brief Read data from the disc image at the given uncompressed offset.
*
* For WUD files this is a direct read. For WUX files this translates
* through the sector index table.
*
* @param reader Reader to read from.
* @param buf Output buffer.
* @param count Number of bytes to read.
* @param offset Byte offset in the uncompressed disc image.
* @return Number of bytes read, or -1 on error.
*/
int64_t wiiu_reader_read_at(WiiuReader *reader, void *buf, size_t count, uint64_t offset);
#ifdef __cplusplus
}
#endif
#endif /* AARUFORMATTOOL_WIIU_READER_H */