diff --git a/tool/wiiu_reader.c b/tool/wiiu_reader.c new file mode 100644 index 0000000..10a4d84 --- /dev/null +++ b/tool/wiiu_reader.c @@ -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 . + * + * WUD/WUX disc reader abstraction for aaruformattool. + */ + +#include "wiiu_reader.h" + +#include +#include +#include + +/** + * @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; +} diff --git a/tool/wiiu_reader.h b/tool/wiiu_reader.h new file mode 100644 index 0000000..05f65ff --- /dev/null +++ b/tool/wiiu_reader.h @@ -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 . + * + * WUD/WUX disc reader abstraction for aaruformattool. + */ + +#ifndef AARUFORMATTOOL_WIIU_READER_H +#define AARUFORMATTOOL_WIIU_READER_H + +#include +#include + +#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 */