├── README.md ├── WudCompress.sln └── WudCompress ├── wud.h ├── WudCompress.vcproj ├── wud.cpp └── main.cpp /README.md: -------------------------------------------------------------------------------- 1 | # WudCompress 2 | A little tool to compress Wii U images/dumps. 3 | -------------------------------------------------------------------------------- /WudCompress.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WudCompress", "WudCompress\WudCompress.vcproj", "{6FC9B74D-FB07-4173-B7CB-86D34867BCD7}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Debug|Win32.Build.0 = Debug|Win32 14 | {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Release|Win32.ActiveCfg = Release|Win32 15 | {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /WudCompress/wud.h: -------------------------------------------------------------------------------- 1 | typedef struct 2 | { 3 | unsigned int magic0; 4 | unsigned int magic1; 5 | unsigned int sectorSize; 6 | unsigned long long uncompressedSize; 7 | unsigned int flags; 8 | }wuxHeader_t; 9 | 10 | typedef struct 11 | { 12 | FILE* fileWud; 13 | long long uncompressedSize; 14 | bool isCompressed; 15 | // data only used when compressed 16 | unsigned int sectorSize; 17 | unsigned int indexTableEntryCount; 18 | unsigned int* indexTable; 19 | long long offsetIndexTable; 20 | long long offsetSectorArray; 21 | }wud_t; 22 | 23 | #define WUX_MAGIC_0 '0XUW' // "WUX0" 24 | #define WUX_MAGIC_1 0x1099d02e 25 | 26 | // wud and wux functions 27 | wud_t* wud_open(char* path); // handles both, compressed and uncompressed files 28 | void wud_close(wud_t* wud); 29 | 30 | unsigned int wud_readData(wud_t* wud, void* buffer, unsigned int length, long long offset); 31 | bool wud_isWUXCompressed(wud_t* wud); 32 | long long wud_getWUDSize(wud_t* wud); -------------------------------------------------------------------------------- /WudCompress/WudCompress.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 25 | 28 | 31 | 34 | 37 | 40 | 49 | 52 | 55 | 58 | 63 | 66 | 69 | 72 | 75 | 78 | 81 | 84 | 85 | 93 | 96 | 99 | 102 | 105 | 108 | 118 | 121 | 124 | 127 | 134 | 137 | 140 | 143 | 146 | 149 | 152 | 155 | 156 | 157 | 158 | 159 | 160 | 165 | 168 | 169 | 172 | 173 | 176 | 177 | 178 | 183 | 184 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /WudCompress/wud.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include"wud.h" 5 | 6 | long long wud_getFileSize64(FILE* file) 7 | { 8 | long long prevSeek = _ftelli64(file); 9 | _fseeki64(file, 0, SEEK_END); 10 | long long fileSize = _ftelli64(file); 11 | _fseeki64(file, prevSeek, SEEK_SET); 12 | return fileSize; 13 | } 14 | 15 | long long wud_getCurrentSeek64(FILE* file) 16 | { 17 | long long currentSeek = _ftelli64(file); 18 | return currentSeek; 19 | } 20 | 21 | void wud_setCurrentSeek64(FILE* file, long long newSeek) 22 | { 23 | _fseeki64(file, newSeek, SEEK_SET); 24 | } 25 | 26 | /* 27 | * Open .wud (Wii U image) or .wux (Wii U compressed image) file 28 | */ 29 | wud_t* wud_open(char* path) 30 | { 31 | FILE* inputFile; 32 | inputFile = fopen(path, "rb"); 33 | if( inputFile == NULL ) 34 | return NULL; 35 | // allocate wud struct 36 | wud_t* wud = (wud_t*)malloc(sizeof(wud_t)); 37 | memset(wud, 0x00, sizeof(wud_t)); 38 | wud->fileWud = inputFile; 39 | // get size of file 40 | long long inputFileSize = wud_getFileSize64(wud->fileWud); 41 | // determine whether the WUD is compressed or not 42 | wuxHeader_t wuxHeader = {0}; 43 | if( fread(&wuxHeader, sizeof(wuxHeader_t), 1, wud->fileWud) != 1 ) 44 | { 45 | // file is too short to be either 46 | wud_close(wud); 47 | return NULL; 48 | } 49 | if( wuxHeader.magic0 == WUX_MAGIC_0 && wuxHeader.magic1 == WUX_MAGIC_1 ) 50 | { 51 | // this is a compressed file 52 | wud->isCompressed = true; 53 | wud->sectorSize = wuxHeader.sectorSize; 54 | wud->uncompressedSize = wuxHeader.uncompressedSize; 55 | // validate header values 56 | if( wud->sectorSize < 0x100 || wud->sectorSize >= 0x10000000 ) 57 | { 58 | wud_close(wud); 59 | return NULL; 60 | } 61 | // calculate offsets and sizes 62 | wud->indexTableEntryCount = (unsigned int)((wud->uncompressedSize+(long long)(wud->sectorSize-1)) / (long long)wud->sectorSize); 63 | wud->offsetIndexTable = wud_getCurrentSeek64(wud->fileWud); 64 | wud->offsetSectorArray = (wud->offsetIndexTable + (long long)wud->indexTableEntryCount*sizeof(unsigned int)); 65 | // align to SECTOR_SIZE 66 | wud->offsetSectorArray = (wud->offsetSectorArray + (long long)(wud->sectorSize-1)); 67 | wud->offsetSectorArray = wud->offsetSectorArray - (wud->offsetSectorArray%(long long)wud->sectorSize); 68 | // read index table 69 | unsigned int indexTableSize = sizeof(unsigned int) * wud->indexTableEntryCount; 70 | wud->indexTable = (unsigned int*)malloc(sizeof(unsigned int) * wud->indexTableEntryCount); 71 | wud_setCurrentSeek64(wud->fileWud, wud->offsetIndexTable); 72 | if( fread(wud->indexTable, sizeof(unsigned int), wud->indexTableEntryCount, wud->fileWud) != wud->indexTableEntryCount ) 73 | { 74 | // could not read index table 75 | wud_close(wud); 76 | return NULL; 77 | } 78 | } 79 | else 80 | { 81 | // uncompressed file 82 | wud->uncompressedSize = inputFileSize; 83 | } 84 | return wud; 85 | } 86 | 87 | /* 88 | * Close wud/wux reader 89 | */ 90 | void wud_close(wud_t* wud) 91 | { 92 | fclose(wud->fileWud); 93 | if( wud->indexTable ) 94 | free(wud->indexTable); 95 | free(wud); 96 | } 97 | 98 | /* 99 | * Read data 100 | * Transparently handles WUX decompression 101 | * Can read up to 4GB-1 at once 102 | */ 103 | unsigned int wud_readData(wud_t* wud, void* buffer, unsigned int length, long long offset) 104 | { 105 | // make sure there is no out-of-bounds read 106 | long long fileBytesLeft = wud->uncompressedSize - offset; 107 | if( fileBytesLeft <= 0 ) 108 | return 0; 109 | if( fileBytesLeft < (long long)length ) 110 | length = (unsigned int)fileBytesLeft; 111 | // read data 112 | unsigned int readBytes = 0; 113 | if( wud->isCompressed == false ) 114 | { 115 | // uncompressed read is just a 1:1 copy 116 | wud_setCurrentSeek64(wud->fileWud, offset); 117 | readBytes = (unsigned int)fread(buffer, 1, length, wud->fileWud); 118 | } 119 | else 120 | { 121 | // compressed read must be handled on a per-sector level 122 | while( length > 0 ) 123 | { 124 | unsigned int sectorOffset = (unsigned int)(offset % (long long)wud->sectorSize); 125 | unsigned int remainingSectorBytes = wud->sectorSize - sectorOffset; 126 | unsigned int sectorIndex = (unsigned int)(offset / (long long)wud->sectorSize); 127 | unsigned int bytesToRead = (remainingSectorBytesindexTable[sectorIndex]; 130 | wud_setCurrentSeek64(wud->fileWud, wud->offsetSectorArray + (long long)sectorIndex*(long long)wud->sectorSize+(long long)sectorOffset); 131 | readBytes += (unsigned int)fread(buffer, 1, bytesToRead, wud->fileWud); 132 | // progress read offset, write pointer and decrease length 133 | buffer = (void*)((char*)buffer + bytesToRead); 134 | length -= bytesToRead; 135 | offset += bytesToRead; 136 | } 137 | } 138 | return readBytes; 139 | } 140 | 141 | /* 142 | * Returns true if the file uses .wux compression, false otherwise 143 | */ 144 | bool wud_isWUXCompressed(wud_t* wud) 145 | { 146 | return wud->isCompressed; 147 | } 148 | 149 | /* 150 | * Returns size of data in bytes 151 | * For .wud: Size of raw file 152 | * For .wux: Size of uncompressed data 153 | */ 154 | long long wud_getWUDSize(wud_t* wud) 155 | { 156 | return wud->uncompressedSize; 157 | } -------------------------------------------------------------------------------- /WudCompress/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include"wud.h" 5 | 6 | /* 7 | * WUX file structure (v1.0): 8 | [Header] 9 | UINT32 magic1 "WUX0" 10 | UINT32 magic2 0x1099d02e 11 | UINT32 sectorSize Size per uncompressed sector (SECTOR_SIZE constant) 12 | UINT64 uncompressedSize Size of the Wii U image before being compressed 13 | UINT32 flags Enable optional parts of the header (not used right now) 14 | 15 | [SectorIndexTable] 16 | UINT32[] lookupIndex table of indices for lookup of each sector. To calculate number of entries in this array: sectorCount = (uncompressedSize+sectorSize-1)/sectorSize 17 | 18 | [SectorData] 19 | UINT8[] padding Padding until the next field (sectorData) is aligned to sectorSize bytes. You can write whatever data you want here 20 | UINT8[] sectorData Array of unique sectors. Size in bytes: sectorSize * sectorCount 21 | 22 | */ 23 | 24 | 25 | #define SECTOR_SIZE (0x8000) 26 | #define SECTOR_HASH_SIZE (32) 27 | 28 | /* 29 | * Hash function used to create a hash of each sector 30 | * The hashes are then compared to find duplicate sectors 31 | */ 32 | void calculateHash256(unsigned char* data, unsigned int length, unsigned char* hashOut) 33 | { 34 | // cheap and simple hash implementation 35 | // you can replace this part with your favorite hash method 36 | memset(hashOut, 0x00, 32); 37 | for(unsigned int i=0; i [-noverify]"); 242 | puts(""); 243 | puts("Parameters:"); 244 | puts("-noverify Skip the file validation step at the end"); 245 | return 0; 246 | } 247 | char* wudPath = argv[1]; 248 | // parse options 249 | bool skipVerify = false; 250 | for(int i=2; i wud) 279 | char* newExtension; 280 | if( wud_isWUXCompressed(wud) ) 281 | { 282 | printf("Mode: Decompress\n"); 283 | newExtension = ".wud"; 284 | } 285 | else 286 | { 287 | printf("Mode: Compress\n"); 288 | newExtension = ".wux"; 289 | } 290 | bool extensionFound = false; 291 | for(int i=strlen(outputPath)-1; i>=0; i--) 292 | { 293 | if( outputPath[i] == '.' ) 294 | { 295 | extensionFound = true; 296 | strcpy(outputPath+i, newExtension); 297 | break; 298 | } 299 | } 300 | if( extensionFound == false ) 301 | strcat(outputPath, newExtension); 302 | // make sure the output file doesn't already exist (avoid accidental overwriting) 303 | FILE* outputFile; 304 | outputFile = fopen(outputPath, "r"); 305 | if( outputFile != NULL ) 306 | { 307 | printf("Output file \"%s\" already exists.\n", outputPath); 308 | wud_close(wud); 309 | return -4; 310 | } 311 | // open output file 312 | outputFile = fopen(outputPath, "wb"); 313 | if( outputFile == NULL ) 314 | { 315 | printf("Unable to create output file\n"); 316 | wud_close(wud); 317 | return -3; 318 | } 319 | printf("Input:\n"); 320 | puts(wudPath); 321 | printf("Output:\n"); 322 | puts(outputPath); 323 | if( wud_isWUXCompressed(wud) ) 324 | { 325 | if( decompressWUD(wud, outputFile, outputPath) == false ) 326 | return -1; 327 | } 328 | else 329 | { 330 | if( compressWUD(wud, outputFile, outputPath) == false ) 331 | return -1; 332 | } 333 | // verify 334 | if( skipVerify == false ) 335 | { 336 | if( validateWUX(wudPath, outputPath) == false ) 337 | { 338 | printf("Validation failed. \"%s\" is corrupted.\n", outputPath); 339 | // delete output file 340 | remove(outputPath); 341 | return -5; 342 | } 343 | else 344 | { 345 | printf("Validation successful. No errors detected.\n"); 346 | } 347 | } 348 | return 0; 349 | } --------------------------------------------------------------------------------