Created
March 30, 2021 18:00
-
-
Save rw-r-r-0644/f0a7e556ce42b6e7dcd82026626c8bd5 to your computer and use it in GitHub Desktop.
ISFS_LoadSuperblock RE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ISFS_Superblock *ISFS_LoadSuperblock(ISFS *isfs) | |
{ | |
if ((isfs->initState & 2) == 0) | |
return; | |
if ((isfs->initState & 0x44) == 0x44) | |
return NULL; | |
int latestVersion = 0; | |
u32 maxGeneration = 0xffffffff; | |
while (true) | |
{ | |
int latestSuperblock = -1; | |
int latestGeneration = 0; | |
/* Try to load the latest ISFS superblock */ | |
if (ISFS_InitSizeProperties(isfs, FALSE) != 0) | |
return NULL; | |
int latestSuperblock = ISFS_FindLatestSuperblock(isfs, FALSE, 0, maxGeneration, 0, &generation, &isfsVersion); | |
if (latestSuperblock >= 0) | |
{ | |
isfs->initState &= ~0x20; | |
latestVersion = isfsVersion; | |
latestGeneration = generation; | |
isfs->nextSuperBlock = (latestSuperblock + 1) & ((1 << isfs->superblockCountLog2) - 1U); | |
} | |
/* Try to load the latest compat ISFS superblock */ | |
if (ISFS_InitSizeProperties(isfs, TRUE) != 0) | |
return NULL; | |
int latestComparSuperblock = | |
ISFS_FindLatestSuperblock(isfs, TRUE, latestGeneration, maxGeneration, 0, &generation, &isfsVersion); | |
if (latestComparSuperblock >= 0) | |
{ | |
isfs->initState |= 0x20; | |
latestVersion = isfsVersion; | |
latestGeneration = generation; | |
isfs->nextSuperBlock = (latestComparSuperblock + 1) & ((1 << isfs->superblockCountLog2) - 1U); | |
latestSuperblock = latestComparSuperblock; | |
} | |
/* Setup size properties according to the current ISFS version */ | |
int rc = ISFS_InitSizeProperties(isfs, (isfs->initState & 0x20) ? TRUE : FALSE); | |
if ((rc != 0) || (latestSuperblock < 0)) | |
return NULL; | |
/* This is a simplification of the many __divdi3 calls. | |
* Results should match, but the code differs | |
* (isfs->nandBlockSize is also involded, although its value can be simplified away) | |
*/ | |
u32 clustersPerSuperblock = 1 << (isfs->metadataSizeLog2 - isfs->superblockCountLog2 - 0xe); | |
u32 dataClusters = (isfs->unkZero2 + isfs->dataAreaSize >> 0xe); | |
u32 superblockClusterIndex = dataClusters + latestSuperblock * clustersPerSuperblock; | |
u32 superblockClusterCount = 1 << (isfs->metadataSizeLog2 - isfs->superblockCountLog2 - 0xe); | |
/* HMAC seed */ | |
*(u32 *)(isfs->field_0x44040 + 0x10) = superblockClusterIndex; | |
rc = ISFS_ReadCluster(isfs, superblockClusterIndex & 0xffff, superblockClusterCount, 2, | |
(int)isfs->field_0x44040, &isfs->superblock, NULL); | |
/* read successfull */ | |
if (rc == 0) | |
break; | |
/* one of the two copies of the hmac hash did not match */ | |
if (rc == -0x80471) | |
{ | |
needsRepair = true; | |
break: | |
} | |
maxGeneration = latestGeneration; | |
} | |
if (latestVersion != 0) | |
{ | |
isfs->initState |= 8; | |
return NULL; | |
} | |
else | |
isfs->initState &= ~8; | |
if (!isfs) | |
return NULL; | |
isfs->initState |= 4; | |
if (needsRepair) | |
ISFS_WriteSuperblock(isfs); | |
return &isfs->superblock; | |
} | |
int ISFS_InitSizeProperties(ISFS *isfs, BOOL isCompat) | |
{ | |
isfs->unkZero2 = isfs->unkZero1; | |
isfs->superblockCountLog2 = (isCompat) ? 4 : 6; | |
if (isfs->nandSizeBytes == 0x4000000) | |
isfs->metadataSizeLog2 = isfs->superblockCountLog2 + 0xe; | |
else if ((isfs->nandSizeBytes == 0x8000000) || (isfs->nandSizeBytes == 0x10000000)) | |
isfs->metadataSizeLog2 = isfs->superblockCountLog2 + 0x10; | |
else if ((isfs->nandSizeBytes == 0x20000000) || (isfs->nandSizeBytes == 0x40000000)) | |
isfs->metadataSizeLog2 = isfs->superblockCountLog2 + 0x12; | |
else | |
return -0x80480; | |
u32 sizeOfSuperblock = 1 << (isfs->metadataSizeLog2 - isfs->superblockCountLog2); | |
if (sizeOfSuperblock > 0x40000) | |
return -0x80480; | |
isfs->dataAreaSize = (isfs->nandSizeBytes - isfs->unkZero2) - (1 << isfs->metadataSizeLog2); | |
isfs->initState = isfs->initState | 0x40; | |
return 0; | |
} | |
u32 ISFS_FindLatestSuperblock(ISFS *isfs, BOOL isCompat, u32 minGeneration, u32 maxGeneration, u32 unkFieldCheck, | |
u32 *outGeneration, u32 *outIsfsVersion) | |
{ | |
*outGeneration = 0; | |
*outIsfsVersion = 0; | |
int lastValidSuperblock = -1; | |
for (u32 superblockIndex = 0; superblockIndex < (1 << isfs->superblockCountLog2); superblockIndex++) | |
{ | |
/* This is a simplification of the many __divdi3 calls. | |
* Results should match, but the code differs | |
* (isfs->nandBlockSize is also involded, although its value can be simplified away) | |
*/ | |
u32 clustersPerSuperblock = 1 << (isfs->metadataSizeLog2 - isfs->superblockCountLog2 - 0xe); | |
u32 dataClusters = (isfs->unkZero2 + isfs->dataAreaSize >> 0xe); | |
u32 superblockClusterIndex = dataClusters + superblockIndex * clustersPerSuperblock; | |
int rc = ISFS_ReadCluster(isfs, superblockClusterIndex, 1, 0, 0, &isfs->superblock, NULL); | |
if ((rc != 0) && (rc != -0x80471)) | |
continue; | |
u32 isfsVersion; | |
if (!isCompat && ((isfs->super.magic & 0xffffff0f) == 0x53465301)) | |
isfsVersion = ((isfs->super.magic >> 4) == 2) ? 0 : 1; | |
else if (isfs->super.magic == 0x53464653) | |
isfsVersion = 0; | |
else | |
continue; | |
if (unkFieldCheck > (isfs->super.unkFieldCheck + 1)) | |
continue; | |
if (unkFieldCheck > isfs->super.unkFieldCheck) | |
isfs->super.unkFieldCheck = unkFieldCheck; | |
if (isfs->super.generation < minGeneration) | |
continue; | |
if (isfs->super.generation >= maxGeneration) | |
continue; | |
if (isfs->super.generation < *outGeneration) | |
continue; | |
*outGeneration = isfs->super.generation; | |
*outIsfsVersion = isfsVersion; | |
lastValidSuperblock = superblockIndex; | |
} | |
return lastValidSuperblock; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment