diff --git a/WudCompress.sln b/WudCompress.sln
new file mode 100644
index 0000000..c10cbf9
--- /dev/null
+++ b/WudCompress.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WudCompress", "WudCompress\WudCompress.vcproj", "{6FC9B74D-FB07-4173-B7CB-86D34867BCD7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Debug|Win32.Build.0 = Debug|Win32
+ {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Release|Win32.ActiveCfg = Release|Win32
+ {6FC9B74D-FB07-4173-B7CB-86D34867BCD7}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/WudCompress/WudCompress.vcproj b/WudCompress/WudCompress.vcproj
new file mode 100644
index 0000000..c47d258
--- /dev/null
+++ b/WudCompress/WudCompress.vcproj
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WudCompress/main.cpp b/WudCompress/main.cpp
new file mode 100644
index 0000000..793b6e2
--- /dev/null
+++ b/WudCompress/main.cpp
@@ -0,0 +1,349 @@
+#include
+#include
+#include
+#include"wud.h"
+
+/*
+ * WUX file structure (v1.0):
+ [Header]
+ UINT32 magic1 "WUX0"
+ UINT32 magic2 0x1099d02e
+ UINT32 sectorSize Size per uncompressed sector (SECTOR_SIZE constant)
+ UINT64 uncompressedSize Size of the Wii U image before being compressed
+ UINT32 flags Enable optional parts of the header (not used right now)
+
+ [SectorIndexTable]
+ UINT32[] lookupIndex table of indices for lookup of each sector. To calculate number of entries in this array: sectorCount = (uncompressedSize+sectorSize-1)/sectorSize
+
+ [SectorData]
+ UINT8[] padding Padding until the next field (sectorData) is aligned to sectorSize bytes. You can write whatever data you want here
+ UINT8[] sectorData Array of unique sectors. Size in bytes: sectorSize * sectorCount
+
+ */
+
+
+#define SECTOR_SIZE (0x8000)
+#define SECTOR_HASH_SIZE (32)
+
+/*
+ * Hash function used to create a hash of each sector
+ * The hashes are then compared to find duplicate sectors
+ */
+void calculateHash256(unsigned char* data, unsigned int length, unsigned char* hashOut)
+{
+ // cheap and simple hash implementation
+ // you can replace this part with your favorite hash method
+ memset(hashOut, 0x00, 32);
+ for(unsigned int i=0; i [-noverify]");
+ puts("");
+ puts("Parameters:");
+ puts("-noverify Skip the file validation step at the end");
+ return 0;
+ }
+ char* wudPath = argv[1];
+ // parse options
+ bool skipVerify = false;
+ for(int i=2; i wud)
+ char* newExtension;
+ if( wud_isWUXCompressed(wud) )
+ {
+ printf("Mode: Decompress\n");
+ newExtension = ".wud";
+ }
+ else
+ {
+ printf("Mode: Compress\n");
+ newExtension = ".wux";
+ }
+ bool extensionFound = false;
+ for(int i=strlen(outputPath)-1; i>=0; i--)
+ {
+ if( outputPath[i] == '.' )
+ {
+ extensionFound = true;
+ strcpy(outputPath+i, newExtension);
+ break;
+ }
+ }
+ if( extensionFound == false )
+ strcat(outputPath, newExtension);
+ // make sure the output file doesn't already exist (avoid accidental overwriting)
+ FILE* outputFile;
+ outputFile = fopen(outputPath, "r");
+ if( outputFile != NULL )
+ {
+ printf("Output file \"%s\" already exists.\n", outputPath);
+ wud_close(wud);
+ return -4;
+ }
+ // open output file
+ outputFile = fopen(outputPath, "wb");
+ if( outputFile == NULL )
+ {
+ printf("Unable to create output file\n");
+ wud_close(wud);
+ return -3;
+ }
+ printf("Input:\n");
+ puts(wudPath);
+ printf("Output:\n");
+ puts(outputPath);
+ if( wud_isWUXCompressed(wud) )
+ {
+ if( decompressWUD(wud, outputFile, outputPath) == false )
+ return -1;
+ }
+ else
+ {
+ if( compressWUD(wud, outputFile, outputPath) == false )
+ return -1;
+ }
+ // verify
+ if( skipVerify == false )
+ {
+ if( validateWUX(wudPath, outputPath) == false )
+ {
+ printf("Validation failed. \"%s\" is corrupted.\n", outputPath);
+ // delete output file
+ remove(outputPath);
+ return -5;
+ }
+ else
+ {
+ printf("Validation successful. No errors detected.\n");
+ }
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/WudCompress/wud.cpp b/WudCompress/wud.cpp
new file mode 100644
index 0000000..eb728a5
--- /dev/null
+++ b/WudCompress/wud.cpp
@@ -0,0 +1,157 @@
+#include
+#include
+#include
+#include"wud.h"
+
+long long wud_getFileSize64(FILE* file)
+{
+ long long prevSeek = _ftelli64(file);
+ _fseeki64(file, 0, SEEK_END);
+ long long fileSize = _ftelli64(file);
+ _fseeki64(file, prevSeek, SEEK_SET);
+ return fileSize;
+}
+
+long long wud_getCurrentSeek64(FILE* file)
+{
+ long long currentSeek = _ftelli64(file);
+ return currentSeek;
+}
+
+void wud_setCurrentSeek64(FILE* file, long long newSeek)
+{
+ _fseeki64(file, newSeek, SEEK_SET);
+}
+
+/*
+ * Open .wud (Wii U image) or .wux (Wii U compressed image) file
+ */
+wud_t* wud_open(char* path)
+{
+ FILE* inputFile;
+ inputFile = fopen(path, "rb");
+ if( inputFile == NULL )
+ return NULL;
+ // allocate wud struct
+ wud_t* wud = (wud_t*)malloc(sizeof(wud_t));
+ memset(wud, 0x00, sizeof(wud_t));
+ wud->fileWud = inputFile;
+ // get size of file
+ long long inputFileSize = wud_getFileSize64(wud->fileWud);
+ // determine whether the WUD is compressed or not
+ wuxHeader_t wuxHeader = {0};
+ if( fread(&wuxHeader, sizeof(wuxHeader_t), 1, wud->fileWud) != 1 )
+ {
+ // file is too short to be either
+ wud_close(wud);
+ return NULL;
+ }
+ if( wuxHeader.magic0 == WUX_MAGIC_0 && wuxHeader.magic1 == WUX_MAGIC_1 )
+ {
+ // this is a compressed file
+ wud->isCompressed = true;
+ wud->sectorSize = wuxHeader.sectorSize;
+ wud->uncompressedSize = wuxHeader.uncompressedSize;
+ // validate header values
+ if( wud->sectorSize < 0x100 || wud->sectorSize >= 0x10000000 )
+ {
+ wud_close(wud);
+ return NULL;
+ }
+ // calculate offsets and sizes
+ wud->indexTableEntryCount = (unsigned int)((wud->uncompressedSize+(long long)(wud->sectorSize-1)) / (long long)wud->sectorSize);
+ wud->offsetIndexTable = wud_getCurrentSeek64(wud->fileWud);
+ wud->offsetSectorArray = (wud->offsetIndexTable + (long long)wud->indexTableEntryCount*sizeof(unsigned int));
+ // align to SECTOR_SIZE
+ wud->offsetSectorArray = (wud->offsetSectorArray + (long long)(wud->sectorSize-1));
+ wud->offsetSectorArray = wud->offsetSectorArray - (wud->offsetSectorArray%(long long)wud->sectorSize);
+ // read index table
+ unsigned int indexTableSize = sizeof(unsigned int) * wud->indexTableEntryCount;
+ wud->indexTable = (unsigned int*)malloc(sizeof(unsigned int) * wud->indexTableEntryCount);
+ wud_setCurrentSeek64(wud->fileWud, wud->offsetIndexTable);
+ if( fread(wud->indexTable, sizeof(unsigned int), wud->indexTableEntryCount, wud->fileWud) != wud->indexTableEntryCount )
+ {
+ // could not read index table
+ wud_close(wud);
+ return NULL;
+ }
+ }
+ else
+ {
+ // uncompressed file
+ wud->uncompressedSize = inputFileSize;
+ }
+ return wud;
+}
+
+/*
+ * Close wud/wux reader
+ */
+void wud_close(wud_t* wud)
+{
+ fclose(wud->fileWud);
+ if( wud->indexTable )
+ free(wud->indexTable);
+ free(wud);
+}
+
+/*
+ * Read data
+ * Transparently handles WUX decompression
+ * Can read up to 4GB-1 at once
+ */
+unsigned int wud_readData(wud_t* wud, void* buffer, unsigned int length, long long offset)
+{
+ // make sure there is no out-of-bounds read
+ long long fileBytesLeft = wud->uncompressedSize - offset;
+ if( fileBytesLeft <= 0 )
+ return 0;
+ if( fileBytesLeft < (long long)length )
+ length = (unsigned int)fileBytesLeft;
+ // read data
+ unsigned int readBytes = 0;
+ if( wud->isCompressed == false )
+ {
+ // uncompressed read is just a 1:1 copy
+ wud_setCurrentSeek64(wud->fileWud, offset);
+ readBytes = (unsigned int)fread(buffer, 1, length, wud->fileWud);
+ }
+ else
+ {
+ // compressed read must be handled on a per-sector level
+ while( length > 0 )
+ {
+ unsigned int sectorOffset = (unsigned int)(offset % (long long)wud->sectorSize);
+ unsigned int remainingSectorBytes = wud->sectorSize - sectorOffset;
+ unsigned int sectorIndex = (unsigned int)(offset / (long long)wud->sectorSize);
+ unsigned int bytesToRead = (remainingSectorBytesindexTable[sectorIndex];
+ wud_setCurrentSeek64(wud->fileWud, wud->offsetSectorArray + (long long)sectorIndex*(long long)wud->sectorSize+(long long)sectorOffset);
+ readBytes += (unsigned int)fread(buffer, 1, bytesToRead, wud->fileWud);
+ // progress read offset, write pointer and decrease length
+ buffer = (void*)((char*)buffer + bytesToRead);
+ length -= bytesToRead;
+ offset += bytesToRead;
+ }
+ }
+ return readBytes;
+}
+
+/*
+ * Returns true if the file uses .wux compression, false otherwise
+ */
+bool wud_isWUXCompressed(wud_t* wud)
+{
+ return wud->isCompressed;
+}
+
+/*
+ * Returns size of data in bytes
+ * For .wud: Size of raw file
+ * For .wux: Size of uncompressed data
+ */
+long long wud_getWUDSize(wud_t* wud)
+{
+ return wud->uncompressedSize;
+}
\ No newline at end of file
diff --git a/WudCompress/wud.h b/WudCompress/wud.h
new file mode 100644
index 0000000..b748d29
--- /dev/null
+++ b/WudCompress/wud.h
@@ -0,0 +1,32 @@
+typedef struct
+{
+ unsigned int magic0;
+ unsigned int magic1;
+ unsigned int sectorSize;
+ unsigned long long uncompressedSize;
+ unsigned int flags;
+}wuxHeader_t;
+
+typedef struct
+{
+ FILE* fileWud;
+ long long uncompressedSize;
+ bool isCompressed;
+ // data only used when compressed
+ unsigned int sectorSize;
+ unsigned int indexTableEntryCount;
+ unsigned int* indexTable;
+ long long offsetIndexTable;
+ long long offsetSectorArray;
+}wud_t;
+
+#define WUX_MAGIC_0 '0XUW' // "WUX0"
+#define WUX_MAGIC_1 0x1099d02e
+
+// wud and wux functions
+wud_t* wud_open(char* path); // handles both, compressed and uncompressed files
+void wud_close(wud_t* wud);
+
+unsigned int wud_readData(wud_t* wud, void* buffer, unsigned int length, long long offset);
+bool wud_isWUXCompressed(wud_t* wud);
+long long wud_getWUDSize(wud_t* wud);
\ No newline at end of file