mirror of
https://github.com/aaru-dps/libaaruformat.git
synced 2026-07-08 18:06:18 +00:00
Add WIIU reader abstraction: implement WUD/WUX file handling and reading logic
This commit is contained in:
185
tool/wiiu_reader.c
Normal file
185
tool/wiiu_reader.c
Normal 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
94
tool/wiiu_reader.h
Normal 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 */
|
||||
Reference in New Issue
Block a user