#include #include #include #include #include #include #include #include #include #include const unsigned int bytesPerSector = 512; const unsigned int rootDirectoryEntryCount = 256; typedef struct { int8_t magic[4]; uint32_t volumeId; uint32_t sectorsPerCluster; uint16_t fatCopies; uint32_t unknown; uint8_t unused[4078]; } __attribute__((packed)) superblock_t; typedef void* fatEntry_t; // Dummy typedef uint16_t fat16Entry_t; typedef uint32_t fat32Entry_t; typedef struct { superblock_t superblock; union { fatEntry_t fatEntries[1]; fat16Entry_t fat16Entries[1]; fat32Entry_t fat32Entries[1]; }; } __attribute__((packed)) fatx_t; typedef struct { uint8_t filenameSize; uint8_t attributes; char filename[42]; //TODO: Meh char seems sort of unportable - should be changed uint32_t firstCluster; uint32_t fileSize; /*52 2 Modification time 54 2 Modification date 56 2 Creation time 58 2 Creation date 60 2 Last access time 62 2 Last access date */ uint16_t timesAndDates[6]; } __attribute__((packed)) directoryEntry_t; /* 0x0000 0x00000000 Free Cluster — also used as parent directory starting cluster in ".." entries for subdirectories of the root directory (FAT12/16) 0x0001 0x00000001 Reserved, do not use 0x0002‑0xFFEF 0x00000002‑0x0FFFFFEF Used cluster; value points to next cluster 0xFFF0‑0xFFF5 0x0FFFFFF0‑0x0FFFFFF5 Reserved in some contexts[11] or also used[3][9][10] 0xFFF6 0x0FFFFFF6 Reserved; do not use[3][9][10][29] 0xFFF7 0x0FFFFFF7 Bad sector in cluster or reserved cluster 0xFFF8‑0xFFFF 0x0FFFFFF8‑0x0FFFFFFF Last cluster in file (EOC) */ void extractFile(bool fat16, fatEntry_t* fatEntries, void* clusterBase, size_t clusterSize, unsigned int cluster, char* path, unsigned int fileSize) { uint8_t* clusterData = (uint8_t*)clusterBase; printf("Extracting '%s' from cluster %i\n",path,cluster); FILE* file = fopen(path,"wb"); while((cluster < fat16?0xFFF0:0x0FFFFFF0) && fileSize) { void* clusterContent = &clusterData[clusterSize * cluster]; size_t chunkSize; if (fileSize < clusterSize) { chunkSize = fileSize; } else { chunkSize = clusterSize; if (fat16) { fat16Entry_t* fat16Entries = (fat16Entry_t*)fatEntries; cluster = fat16Entries[cluster]; } else { fat32Entry_t* fat32Entries = (fat32Entry_t*)fatEntries; cluster = fat32Entries[cluster]; } } fwrite(clusterContent,1,chunkSize,file); fileSize -= chunkSize; printf("Wrote one chunk, now going to cluster %i, %i bytes left\n",cluster,fileSize); } fclose(file); } void extractDirectory(bool fat16, fatEntry_t* fatEntries, void* clusterBase, size_t clusterSize, unsigned int cluster, char* path, unsigned int entryCount) { // Create the directory mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); // Start to fill it uint8_t* clusterData = (uint8_t*)clusterBase; directoryEntry_t* entries = (directoryEntry_t*)&clusterData[cluster * clusterSize]; unsigned int i; for(i = 0; i < entryCount; i++) { char newPath[4096]; //TODO: allocate on stack on demand directoryEntry_t* entry = &entries[i]; if (entry->filenameSize == 0xE5) { printf("[deleted file]\n"); } else if (entry->filenameSize == 0xFF) { printf("[end of directory]\n"); break; // continue; } else { // Get filename char filename[42+1]; strncpy(filename,entry->filename,entry->filenameSize); filename[entry->filenameSize] = '\0'; // Create a path for the entry strcpy(newPath,path); strcat(newPath,filename); // Debug message printf("Cluster %i is called %i '%s' (extracting to '%s'), firstcluster is %i\n",i,entry->filenameSize,entry->filename,newPath,entry->firstCluster); // Check if new attributes are used if (entry->attributes & (~0x30)) { //TODO: Use dir condition instead printf("Unknown attributes included: 0x%02X!\n",entry->attributes); while(1); } //TODO: Print all attributes // Extract file or directory if (entry->attributes & 0x10) { //TODO: Use dir condition instead printf("Trying to open dir\n"); strcat(newPath,"/"); extractDirectory(fat16, fatEntries, clusterBase, clusterSize, entry->firstCluster, newPath, -1); //TODO: what if this cluster has a following one? } else { extractFile(fat16, fatEntries, clusterBase, clusterSize, entry->firstCluster, newPath, entry->fileSize); } } } } int main(int argc, char* argv[]) { uint8_t* buffer; size_t length; // Parse arguments if ((argc != 3) && (argc != 4)) { printf("%s []\n",argv[0]); } char* source = argv[1]; char* path = argv[2]; char* size = ""; if (argc == 4) { size = argv[3]; } // Load the source file /* { FILE* bin = fopen(source,"rb"); fseek(bin,0,SEEK_END); length = ftell(bin); fseek(bin,0,SEEK_SET); buffer = malloc(length); fread(buffer,1,length,bin); fclose(bin); printf("Read %i bytes from file!\n",(int)length); } */ int file = open(source, O_RDONLY); length = lseek(file, 0, SEEK_END); buffer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, file, 0); fatx_t* fatx = (fatx_t*)buffer; unsigned int partitionSize = length - sizeof(superblock_t); if (sscanf(size, "0x%X", &partitionSize) != 1) { sscanf(size, "%u", &partitionSize); } size_t fatEntrySize; size_t clusterSize = fatx->superblock.sectorsPerCluster * bytesPerSector; unsigned int clusterCount = partitionSize / clusterSize; bool fat16 = clusterCount < 65525; if (fat16) { fatEntrySize = sizeof(fat16Entry_t); } else { fatEntrySize = sizeof(fat32Entry_t); } printf("Partition-Size: %u\n",partitionSize); printf("Cluster sector count is %u\n",fatx->superblock.sectorsPerCluster); printf("FAT Copies: %u (Should be 1)\n",fatx->superblock.fatCopies); printf("Unknown: %u\n",fatx->superblock.unknown); //((69632-4096)/2)*16384 = ps size_t fatSize = clusterCount * fatEntrySize; printf("Fat size is %lu / 0x%lX\n",fatSize,fatSize); size_t fatxSize = sizeof(superblock_t) + ((fatSize + 0xFFF) & (~0xFFF)); // Xbox-Linux describes this with 0x1000 alignment, I chose a cluster.. printf("Fatx size is %lu / 0x%lX\n",fatxSize,fatxSize); printf("Cluster size is %lu / 0x%lX\n",clusterSize,clusterSize); //(directoryEntry_t*)((uintptr_t)buffer + 0x11000); // uint8_t* clusterBase = (uint8_t*)((uintptr_t)fatx + fatxSize - clusterSize); // All clusters are starting at 1, we fix this by starting moving our array one index down char newPath[4096]; strcpy(newPath,path); if (newPath[strlen(newPath) - 1] != '/') { strcat(newPath,"/"); } extractDirectory(fat16, fatx->fatEntries, clusterBase, clusterSize, 1, newPath, rootDirectoryEntryCount); //TODO: Why do I have to advance one cluster? Why isn't this in the first one (which would be 0)? printf("Done!\n"); return 0; }