You've already forked pico-loader
mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-01-09 16:28:35 -08:00
Initial commit
This commit is contained in:
235
arm7/source/loader/DsiWareSaveArranger.cpp
Normal file
235
arm7/source/loader/DsiWareSaveArranger.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include <memory>
|
||||
#include "DsiWareSaveArranger.h"
|
||||
|
||||
bool DsiWareSaveArranger::SetupDsiWareSave(const TCHAR* romPath, const nds_header_twl_t& romHeader, DsiWareSaveResult& result) const
|
||||
{
|
||||
char path[256];
|
||||
strcpy(path, romPath);
|
||||
if (!CreateDeviceListPath(path, result.romFilePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (romHeader.twlPrivateSavSize != 0)
|
||||
{
|
||||
char* extension = strrchr(path, '.');
|
||||
if (!extension)
|
||||
extension = &path[strlen(path)];
|
||||
extension[0] = '.';
|
||||
extension[1] = 'p';
|
||||
extension[2] = 'r';
|
||||
extension[3] = 'v';
|
||||
extension[4] = 0;
|
||||
if (!SetupDsiWareSaveFile(path, romHeader.twlPrivateSavSize) ||
|
||||
!CreateDeviceListPath(path, result.privateSavePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (romHeader.twlPublicSavSize != 0)
|
||||
{
|
||||
strcpy(path, romPath);
|
||||
char* extension = strrchr(path, '.');
|
||||
if (!extension)
|
||||
extension = &path[strlen(path)];
|
||||
extension[0] = '.';
|
||||
extension[1] = 'p';
|
||||
extension[2] = 'u';
|
||||
extension[3] = 'b';
|
||||
extension[4] = 0;
|
||||
if (!SetupDsiWareSaveFile(path, romHeader.twlPublicSavSize) ||
|
||||
!CreateDeviceListPath(path, result.publicSavePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DsiWareSaveArranger::SetupDsiWareSaveFile(const TCHAR* savePath, u32 saveSize) const
|
||||
{
|
||||
if (saveSize == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto file = std::make_unique<FIL>();
|
||||
if (f_open(file.get(), savePath, FA_OPEN_ALWAYS | FA_READ | FA_WRITE) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to open or create save file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 initialSize = f_size(file.get());
|
||||
if (initialSize < saveSize)
|
||||
{
|
||||
if (f_lseek(file.get(), saveSize) != FR_OK ||
|
||||
f_lseek(file.get(), 0) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to create private save file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fatHeader = CreateFatHeader(saveSize);
|
||||
|
||||
UINT bytesWritten = 0;
|
||||
if (f_write(file.get(), fatHeader.get(), sizeof(fat_header_t), &bytesWritten) != FR_OK ||
|
||||
bytesWritten != sizeof(fat_header_t))
|
||||
{
|
||||
LOG_FATAL("Failed to format private save file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(fatHeader.get(), 0, 512);
|
||||
|
||||
while (f_tell(file.get()) < saveSize)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
if (f_write(file.get(), fatHeader.get(), 512, &bytesWritten) != FR_OK ||
|
||||
bytesWritten != 512)
|
||||
{
|
||||
LOG_FATAL("Failed to format private save file\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f_close(file.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<DsiWareSaveArranger::fat_header_t> DsiWareSaveArranger::CreateFatHeader(u32 saveSize) const
|
||||
{
|
||||
// based on https://github.com/Epicpkmn11/NTM/blob/master/arm9/src/sav.c
|
||||
const u32 maxSectors = saveSize >> 9;
|
||||
u32 sectorCount = 1;
|
||||
u32 secPerTrk = 1;
|
||||
u32 numHeads = 1;
|
||||
u32 sectorCountNext = 0;
|
||||
while (sectorCountNext <= maxSectors)
|
||||
{
|
||||
sectorCountNext = secPerTrk * (numHeads + 1) * (numHeads + 1);
|
||||
if (sectorCountNext <= maxSectors)
|
||||
{
|
||||
numHeads++;
|
||||
sectorCount = sectorCountNext;
|
||||
|
||||
secPerTrk++;
|
||||
sectorCountNext = secPerTrk * numHeads * numHeads;
|
||||
if (sectorCountNext <= maxSectors)
|
||||
{
|
||||
sectorCount = sectorCountNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
sectorCountNext = (secPerTrk + 1) * numHeads * numHeads;
|
||||
if (sectorCountNext <= maxSectors)
|
||||
{
|
||||
secPerTrk++;
|
||||
sectorCount = sectorCountNext;
|
||||
}
|
||||
|
||||
u32 sectorsPerCluster;
|
||||
u32 totalClusters;
|
||||
if (sectorCount > 8192)
|
||||
{
|
||||
sectorsPerCluster = 8;
|
||||
totalClusters = (sectorCount + 7) >> 3;
|
||||
}
|
||||
else if (sectorCount > 1024)
|
||||
{
|
||||
sectorsPerCluster = 4;
|
||||
totalClusters = (sectorCount + 3) >> 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sectorsPerCluster = 1;
|
||||
totalClusters = sectorCount;
|
||||
}
|
||||
|
||||
u32 fatSizeInBytes = ((totalClusters + 1) >> 1) * 3; // 2 sectors -> 3 byte
|
||||
|
||||
auto fatHeader = std::make_unique<fat_header_t>();
|
||||
|
||||
fatHeader->jumpInstruction[0] = 0xE9;
|
||||
fatHeader->jumpInstruction[1] = 0;
|
||||
fatHeader->jumpInstruction[2] = 0;
|
||||
memcpy(fatHeader->oemName, "MSWIN4.1", 8);
|
||||
fatHeader->bytesPerSector = 512;
|
||||
fatHeader->sectorsPerCluster = sectorsPerCluster;
|
||||
fatHeader->reservedSectors = 1;
|
||||
fatHeader->numberOfFats = 2;
|
||||
fatHeader->rootEntries = saveSize < 0x8C000 ? 32 : 512;
|
||||
fatHeader->totalSectorsSmall = sectorCount;
|
||||
fatHeader->mediaType = 0xF8; // "hard drive"
|
||||
fatHeader->fatSectorCount = (fatSizeInBytes + 511) >> 9;
|
||||
fatHeader->sectorsPerTrack = secPerTrk;
|
||||
fatHeader->numberOfHeads = numHeads;
|
||||
fatHeader->hiddenSectors = 0;
|
||||
fatHeader->totalSectorsLarge = 0;
|
||||
fatHeader->diskNumber = 5;
|
||||
fatHeader->BS_Reserved1 = 0;
|
||||
fatHeader->bootSignature = 0x29;
|
||||
fatHeader->volumeSerialNumber = 0x12345678;
|
||||
memcpy(fatHeader->volumeLabel, "VOLUMELABEL", 11);
|
||||
memcpy(fatHeader->fileSystemType, "FAT12 ", 8);
|
||||
memset(fatHeader->bootCode, 0, sizeof(fatHeader->bootCode));
|
||||
fatHeader->endOfSectorMarker = 0xAA55;
|
||||
|
||||
return fatHeader;
|
||||
}
|
||||
|
||||
bool DsiWareSaveArranger::CreateDeviceListPath(TCHAR* savePath, char* deviceListPath) const
|
||||
{
|
||||
auto fileInfo = std::make_unique<FILINFO>();
|
||||
strcpy(deviceListPath, "nand:/");
|
||||
char* shortPath = deviceListPath + 6;
|
||||
char* currentPathSegment = strchr(savePath, '/');
|
||||
do
|
||||
{
|
||||
currentPathSegment = strchr(currentPathSegment + 1, '/');
|
||||
if (currentPathSegment)
|
||||
{
|
||||
*currentPathSegment = 0;
|
||||
}
|
||||
|
||||
if (f_stat(savePath, fileInfo.get()) != FR_OK)
|
||||
{
|
||||
LOG_DEBUG("Failed to create short path\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG("%s\n", savePath);
|
||||
// Use fname when altname is empty to handle short filenames.
|
||||
const char* nameToUse = fileInfo->altname[0]
|
||||
? fileInfo->altname
|
||||
: fileInfo->fname;
|
||||
u32 length = strlen(nameToUse);
|
||||
if (shortPath + length - deviceListPath >= 64)
|
||||
{
|
||||
LOG_DEBUG("Path too long\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(shortPath, nameToUse);
|
||||
shortPath += length;
|
||||
if (currentPathSegment)
|
||||
{
|
||||
if (shortPath + 1 - deviceListPath >= 64)
|
||||
{
|
||||
LOG_DEBUG("Path too long\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*shortPath++ = '/';
|
||||
*currentPathSegment = '/';
|
||||
}
|
||||
} while (currentPathSegment);
|
||||
LOG_DEBUG("%s\n", deviceListPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user