├── 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 | }
--------------------------------------------------------------------------------