mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 09:16:49 +02:00
Initial commit
This commit is contained in:
24
arm7/source/loader/ApListFactory.cpp
Normal file
24
arm7/source/loader/ApListFactory.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "common.h"
|
||||
#include "ApListFactory.h"
|
||||
|
||||
ApList* ApListFactory::CreateFromFile(const TCHAR *path)
|
||||
{
|
||||
FIL file;
|
||||
if (f_open(&file, path, FA_OPEN_EXISTING | FA_READ) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to open ap list file\n");
|
||||
return nullptr;
|
||||
}
|
||||
const u32 entryCount = f_size(&file) / sizeof(ApListEntry);
|
||||
auto entries = std::make_unique_for_overwrite<ApListEntry[]>(entryCount);
|
||||
UINT bytesRead = 0;
|
||||
FRESULT result = f_read(&file, entries.get(), entryCount * sizeof(ApListEntry), &bytesRead);
|
||||
if (result != FR_OK || bytesRead != entryCount * sizeof(ApListEntry))
|
||||
{
|
||||
LOG_FATAL("Failed to read ap list file\n");
|
||||
return nullptr;
|
||||
}
|
||||
f_close(&file);
|
||||
|
||||
return new ApList(std::move(entries), entryCount);
|
||||
}
|
||||
12
arm7/source/loader/ApListFactory.h
Normal file
12
arm7/source/loader/ApListFactory.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "ApList.h"
|
||||
|
||||
/// @brief Factory for creating \see ApList instances.
|
||||
class ApListFactory
|
||||
{
|
||||
public:
|
||||
/// @brief Creates an \see ApList instance from the file at the given \p path.
|
||||
/// @param path The ap list file path.
|
||||
/// @return A pointer to the constructed \see ApList instance, or \c nullptr if construction failed.
|
||||
ApList* CreateFromFile(const TCHAR* path);
|
||||
};
|
||||
151
arm7/source/loader/Arm7IoRegisterClearer.cpp
Normal file
151
arm7/source/loader/Arm7IoRegisterClearer.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/dma/dmaNitro.h>
|
||||
#include <libtwl/dma/dmaTwl.h>
|
||||
#include <libtwl/gfx/gfxStatus.h>
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/sio/sio.h>
|
||||
#include <libtwl/sound/sound.h>
|
||||
#include <libtwl/sound/soundCapture.h>
|
||||
#include <libtwl/sound/soundChannel.h>
|
||||
#include <libtwl/sys/sysPower.h>
|
||||
#include <libtwl/timer/timer.h>
|
||||
#include "SdmmcDefinitions.h"
|
||||
#include "core/Environment.h"
|
||||
#include "Arm7IoRegisterClearer.h"
|
||||
|
||||
void Arm7IoRegisterClearer::ClearIoRegisters() const
|
||||
{
|
||||
REG_IME = 0;
|
||||
REG_IE = 0;
|
||||
if (Environment::IsDsiMode())
|
||||
{
|
||||
ClearTwlIoRegisters();
|
||||
}
|
||||
ClearNtrIoRegisters();
|
||||
}
|
||||
|
||||
void Arm7IoRegisterClearer::ClearNtrIoRegisters() const
|
||||
{
|
||||
REG_DISPSTAT = 0;
|
||||
REG_TM0CNT_H = 0; // timer 0
|
||||
REG_TM0CNT_L = 0;
|
||||
REG_TM1CNT_H = 0; // timer 1
|
||||
REG_TM1CNT_L = 0;
|
||||
REG_TM2CNT_H = 0; // timer 2
|
||||
REG_TM2CNT_L = 0;
|
||||
REG_TM3CNT_H = 0; // timer 3
|
||||
REG_TM3CNT_L = 0;
|
||||
REG_DMA0CNT = 0; // dma 0
|
||||
REG_DMA0SAD = 0;
|
||||
REG_DMA0DAD = 0;
|
||||
REG_DMA1CNT = 0; // dma 1
|
||||
REG_DMA1SAD = 0;
|
||||
REG_DMA1DAD = 0;
|
||||
REG_DMA2CNT = 0; // dma 2
|
||||
REG_DMA2SAD = 0;
|
||||
REG_DMA2DAD = 0;
|
||||
REG_DMA3CNT = 0; // dma 3
|
||||
REG_DMA3SAD = 0;
|
||||
REG_DMA3DAD = 0;
|
||||
REG_RCNT0_L = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
REG_SOUNDxCNT(i) = 0;
|
||||
REG_SOUNDxSAD(i) = 0;
|
||||
REG_SOUNDxTMR(i) = 0;
|
||||
REG_SOUNDxPNT(i) = 0;
|
||||
REG_SOUNDxLEN(i) = 0;
|
||||
}
|
||||
|
||||
REG_SOUNDCNT = 0;
|
||||
REG_SNDCAP0CNT = 0;
|
||||
REG_SNDCAP1CNT = 0;
|
||||
|
||||
REG_SNDCAP0DAD = 0;
|
||||
REG_SNDCAP0LEN = 0;
|
||||
REG_SNDCAP1DAD = 0;
|
||||
REG_SNDCAP1LEN = 0;
|
||||
|
||||
sys_setWifiPower(false);
|
||||
sys_setSoundPower(true);
|
||||
}
|
||||
|
||||
void Arm7IoRegisterClearer::ClearTwlIoRegisters() const
|
||||
{
|
||||
REG_IE2 = 0;
|
||||
REG_NDMA0SAD = 0;
|
||||
REG_NDMA0DAD = 0;
|
||||
REG_NDMA0TCNT = 0;
|
||||
REG_NDMA0WCNT = 0;
|
||||
REG_NDMA0BCNT = 0;
|
||||
REG_NDMA0FDATA = 0;
|
||||
REG_NDMA0CNT = 0;
|
||||
REG_NDMA1SAD = 0;
|
||||
REG_NDMA1DAD = 0;
|
||||
REG_NDMA1TCNT = 0;
|
||||
REG_NDMA1WCNT = 0;
|
||||
REG_NDMA1BCNT = 0;
|
||||
REG_NDMA1FDATA = 0;
|
||||
REG_NDMA1CNT = 0;
|
||||
REG_NDMA2SAD = 0;
|
||||
REG_NDMA2DAD = 0;
|
||||
REG_NDMA2TCNT = 0;
|
||||
REG_NDMA2WCNT = 0;
|
||||
REG_NDMA2BCNT = 0;
|
||||
REG_NDMA2FDATA = 0;
|
||||
REG_NDMA2CNT = 0;
|
||||
REG_NDMA3SAD = 0;
|
||||
REG_NDMA3DAD = 0;
|
||||
REG_NDMA3TCNT = 0;
|
||||
REG_NDMA3WCNT = 0;
|
||||
REG_NDMA3BCNT = 0;
|
||||
REG_NDMA3FDATA = 0;
|
||||
REG_NDMA3CNT = 0;
|
||||
|
||||
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDSTATUS0) = 0;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDSTATUS1) = 0;
|
||||
*(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512;
|
||||
*(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0;
|
||||
|
||||
*(vu16*)(0x04004A00 + REG_SDDATACTL32) &= 0xF7FFu;
|
||||
*(vu16*)(0x04004A00 + REG_SDDATACTL32) &= 0xEFFFu;
|
||||
*(vu16*)(0x04004A00 + REG_SDDATACTL32) |= 0x402u;
|
||||
*(vu16*)(0x04004A00 + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2;
|
||||
*(vu16*)(0x04004A00 + REG_SDDATACTL32) &= 0xFFFFu;
|
||||
*(vu16*)(0x04004A00 + REG_SDDATACTL) &= 0xFFDFu;
|
||||
*(vu16*)(0x04004A00 + REG_SDBLKLEN32) = 512;
|
||||
*(vu16*)(0x04004A00 + REG_SDBLKCOUNT32) = 1;
|
||||
*(vu16*)(0x04004A00 + REG_SDRESET) &= 0xFFFEu;
|
||||
*(vu16*)(0x04004A00 + REG_SDRESET) |= 1u;
|
||||
*(vu16*)(0x04004A00 + REG_SDIRMASK0) |= TMIO_MASK_ALL;
|
||||
*(vu16*)(0x04004A00 + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16;
|
||||
*(vu16*)(0x04004A00 + REG_SDSTATUS0) = 0;
|
||||
*(vu16*)(0x04004A00 + REG_SDSTATUS1) = 0;
|
||||
*(vu16*)(0x04004A00 + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(vu16*)(0x04004A00 + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(vu16*)(0x04004A00 + REG_SDPORTSEL) &= 0xFFFCu;
|
||||
*(vu16*)(0x04004A00 + REG_SDCLKCTL) = 0x20;
|
||||
*(vu16*)(0x04004A00 + REG_SDOPT) = 0x40EE;
|
||||
*(vu16*)(0x04004A00 + REG_SDPORTSEL) &= 0xFFFCu;
|
||||
*(vu16*)(0x04004A00 + REG_SDBLKLEN) = 512;
|
||||
*(vu16*)(0x04004A00 + REG_SDSTOP) = 0;
|
||||
}
|
||||
13
arm7/source/loader/Arm7IoRegisterClearer.h
Normal file
13
arm7/source/loader/Arm7IoRegisterClearer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Class for clearing the arm9 IO registers.
|
||||
class Arm7IoRegisterClearer
|
||||
{
|
||||
public:
|
||||
/// @brief Clears the arm7 IO registers.
|
||||
void ClearIoRegisters() const;
|
||||
|
||||
private:
|
||||
void ClearNtrIoRegisters() const;
|
||||
void ClearTwlIoRegisters() const;
|
||||
};
|
||||
113
arm7/source/loader/Blowfish.cpp
Normal file
113
arm7/source/loader/Blowfish.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "common.h"
|
||||
#include <bit>
|
||||
#include "Blowfish.h"
|
||||
|
||||
void Blowfish::Encrypt(const void* src, void* dst, u32 length) const
|
||||
{
|
||||
const u32* src32 = (const u32*)src;
|
||||
u32* dst32 = (u32*)dst;
|
||||
for (u32 i = 0; i < (length / 4); i += 2)
|
||||
{
|
||||
u64 value = src32[i] | ((u64)src32[i + 1] << 32);
|
||||
value = Encrypt(value);
|
||||
dst32[i] = value & 0xFFFFFFFFu;
|
||||
dst32[i + 1] = value >> 32;
|
||||
}
|
||||
}
|
||||
|
||||
u64 Blowfish::Encrypt(u64 value) const
|
||||
{
|
||||
u32 y = value & 0xFFFFFFFFu;
|
||||
u32 x = value >> 32;
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
{
|
||||
u32 z = _keyTable.pTable[i] ^ x;
|
||||
u32 a = _keyTable.sBoxes[0][(z >> 24) & 0xFF];
|
||||
u32 b = _keyTable.sBoxes[1][(z >> 16) & 0xFF];
|
||||
u32 c = _keyTable.sBoxes[2][(z >> 8) & 0xFF];
|
||||
u32 d = _keyTable.sBoxes[3][z & 0xFF];
|
||||
x = (d + (c ^ (b + a))) ^ y;
|
||||
y = z;
|
||||
}
|
||||
|
||||
return (x ^ _keyTable.pTable[16]) | ((u64)(y ^ _keyTable.pTable[17]) << 32);
|
||||
}
|
||||
|
||||
void Blowfish::Decrypt(const void* src, void* dst, u32 length) const
|
||||
{
|
||||
const u32* src32 = (const u32*)src;
|
||||
u32* dst32 = (u32*)dst;
|
||||
for (u32 i = 0; i < (length / 4); i += 2)
|
||||
{
|
||||
u64 value = src32[i] | ((u64)src32[i + 1] << 32);
|
||||
value = Decrypt(value);
|
||||
dst32[i] = value & 0xFFFFFFFFu;
|
||||
dst32[i + 1] = value >> 32;
|
||||
}
|
||||
}
|
||||
|
||||
u64 Blowfish::Decrypt(u64 value) const
|
||||
{
|
||||
u32 y = value & 0xFFFFFFFFu;
|
||||
u32 x = value >> 32;
|
||||
for (u32 i = 17; i >= 2; i--)
|
||||
{
|
||||
u32 z = _keyTable.pTable[i] ^ x;
|
||||
u32 a = _keyTable.sBoxes[0][(z >> 24) & 0xFF];
|
||||
u32 b = _keyTable.sBoxes[1][(z >> 16) & 0xFF];
|
||||
u32 c = _keyTable.sBoxes[2][(z >> 8) & 0xFF];
|
||||
u32 d = _keyTable.sBoxes[3][z & 0xFF];
|
||||
x = (d + (c ^ (b + a))) ^ y;
|
||||
y = z;
|
||||
}
|
||||
|
||||
return (x ^ _keyTable.pTable[1]) | ((u64)(y ^ _keyTable.pTable[0]) << 32);
|
||||
}
|
||||
|
||||
void Blowfish::TransformTable(u32 idCode, int level, int modulo)
|
||||
{
|
||||
u32 keyCode[3] = { idCode, idCode >> 1, idCode << 1 };
|
||||
if (level >= 1)
|
||||
{
|
||||
ApplyKeyCode(&keyCode[0], modulo);
|
||||
}
|
||||
if (level >= 2)
|
||||
{
|
||||
ApplyKeyCode(&keyCode[0], modulo);
|
||||
}
|
||||
keyCode[1] <<= 1;
|
||||
keyCode[2] >>= 1;
|
||||
if (level >= 3)
|
||||
{
|
||||
ApplyKeyCode(&keyCode[0], modulo);
|
||||
}
|
||||
}
|
||||
|
||||
void Blowfish::ApplyKeyCode(u32* keyCode, int modulo)
|
||||
{
|
||||
Encrypt(&keyCode[1], &keyCode[1], 8);
|
||||
Encrypt(&keyCode[0], &keyCode[0], 8);
|
||||
const u32 reversedKeyCode[3] =
|
||||
{
|
||||
std::byteswap(keyCode[0]),
|
||||
std::byteswap(keyCode[1]),
|
||||
std::byteswap(keyCode[2])
|
||||
};
|
||||
int keyCodeIndex = 0;
|
||||
for (u32 i = 0; i < BLOWFISH_PTABLE_ENTRY_COUNT; i++)
|
||||
{
|
||||
_keyTable.pTable[i] ^= reversedKeyCode[keyCodeIndex];
|
||||
if (++keyCodeIndex == (modulo >> 2))
|
||||
{
|
||||
keyCodeIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u64 scratch = 0;
|
||||
u64* keyTable = (u64*)&_keyTable;
|
||||
for (u32 i = 0; i < (sizeof(KeyTable) / 8); i++)
|
||||
{
|
||||
scratch = Encrypt(scratch);
|
||||
keyTable[i] = (scratch >> 32) | ((scratch & 0xFFFFFFFFu) << 32);
|
||||
}
|
||||
}
|
||||
67
arm7/source/loader/Blowfish.h
Normal file
67
arm7/source/loader/Blowfish.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
|
||||
#define BLOWFISH_PTABLE_ENTRY_COUNT 18
|
||||
#define BLOWFISH_SBOX_COUNT 4
|
||||
#define BLOWFISH_SBOX_ENTRY_COUNT 256
|
||||
|
||||
/// @brief Class for blowfish encryption and decryption.
|
||||
class Blowfish
|
||||
{
|
||||
public:
|
||||
/// @brief Struct representing a blowfish key table.
|
||||
struct KeyTable
|
||||
{
|
||||
u32 pTable[BLOWFISH_PTABLE_ENTRY_COUNT];
|
||||
u32 sBoxes[BLOWFISH_SBOX_COUNT][BLOWFISH_SBOX_ENTRY_COUNT];
|
||||
};
|
||||
|
||||
static_assert(sizeof(KeyTable) == 0x1048, "Invalid size of Blowfish::KeyTable.");
|
||||
|
||||
/// @brief Constructs an instance of \see Blowfish using the given \p keyTable.
|
||||
/// @param keyTable The key table to use. A copy will be made into the constructed class.
|
||||
explicit Blowfish(const KeyTable* keyTable)
|
||||
: Blowfish(keyTable->pTable, keyTable->sBoxes) { }
|
||||
|
||||
/// @brief Constructs an instance of \see Blowfish using the given \p pTable and \p sBoxes.
|
||||
/// @param pTable The p table to use. A copy will be made into the constructed class.
|
||||
/// @param sBoxes The s boxes to use. A copy will be made into the constructed class.
|
||||
Blowfish(const void* pTable, const void* sBoxes)
|
||||
{
|
||||
memcpy(_keyTable.pTable, pTable, sizeof(_keyTable.pTable));
|
||||
memcpy(_keyTable.sBoxes, sBoxes, sizeof(_keyTable.sBoxes));
|
||||
}
|
||||
|
||||
/// @brief Encrypts the given \p length from \p src to \p dst.
|
||||
/// @param src The source buffer.
|
||||
/// @param dst The encrypted destination buffer.
|
||||
/// @param length The length of the data to encrypt. Must be a multiple of 8.
|
||||
void Encrypt(const void* src, void* dst, u32 length) const;
|
||||
|
||||
/// @brief Encrypts the given 64-bit \p value and returns the result.
|
||||
/// @param value The 64-bit value to encrypt.
|
||||
/// @return The encrypted result.
|
||||
u64 Encrypt(u64 value) const;
|
||||
|
||||
/// @brief Drcrypts the given \p length from \p src to \p dst.
|
||||
/// @param src The encrypted source buffer.
|
||||
/// @param dst The decrypted destination buffer.
|
||||
/// @param length The length of the data to encrypt. Must be a multiple of 8.
|
||||
void Decrypt(const void* src, void* dst, u32 length) const;
|
||||
|
||||
/// @brief Decrypts the given 64-bit \p value and returns the result.
|
||||
/// @param value The 64-bit value to decrypt.
|
||||
/// @return The decrypted result.
|
||||
u64 Decrypt(u64 value) const;
|
||||
|
||||
/// @brief Transforms the key table by the given \p idCode, \p level and \p modulo.
|
||||
/// @param idCode The id code to use.
|
||||
/// @param level The transform level to use.
|
||||
/// @param modulo The modulo to use.
|
||||
void TransformTable(u32 idCode, int level, int modulo);
|
||||
|
||||
private:
|
||||
void ApplyKeyCode(u32* keyCode, int modulo);
|
||||
|
||||
KeyTable _keyTable;
|
||||
};
|
||||
14
arm7/source/loader/BootMode.h
Normal file
14
arm7/source/loader/BootMode.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief The Pico Loader boot mode.
|
||||
enum class BootMode
|
||||
{
|
||||
/// @brief Boot a retail or homebrew rom.
|
||||
Normal,
|
||||
|
||||
/// @brief Reboot a retail rom that used OS_ResetSystem.
|
||||
SdkResetSystem,
|
||||
|
||||
/// @brief Boot a multiboot rom that is already loaded into memory.
|
||||
Multiboot
|
||||
};
|
||||
107
arm7/source/loader/CardSaveArranger.cpp
Normal file
107
arm7/source/loader/CardSaveArranger.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include "SaveList.h"
|
||||
#include "SaveListFactory.h"
|
||||
#include "fileInfo.h"
|
||||
#include "CardSaveArranger.h"
|
||||
|
||||
#define SAVE_LIST_PATH "/_pico/savelist.bin"
|
||||
#define DEFAULT_SAVE_SIZE (512 * 1024)
|
||||
#define SAVE_FILL_VALUE 0xFF
|
||||
|
||||
bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
||||
{
|
||||
SaveList* saveList = SaveListFactory().CreateFromFile(SAVE_LIST_PATH);
|
||||
u32 saveSize = DEFAULT_SAVE_SIZE;
|
||||
if (saveList)
|
||||
{
|
||||
const auto saveListEntry = saveList->FindEntry(gameCode);
|
||||
if (!saveListEntry)
|
||||
{
|
||||
LOG_WARNING("Game code %c%c%c%c not found in save list\n",
|
||||
gameCode & 0xFF, (gameCode >> 8) & 0xFF, (gameCode >> 16) & 0xFF, gameCode >> 24);
|
||||
}
|
||||
else
|
||||
{
|
||||
saveSize = saveListEntry->GetSaveSize();
|
||||
saveListEntry->Dump();
|
||||
}
|
||||
delete saveList;
|
||||
}
|
||||
if (saveSize == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto file = std::make_unique<FIL>();
|
||||
LOG_DEBUG("Save file: %s\n", savePath);
|
||||
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(), initialSize) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to expand save file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ffBuffer = std::make_unique<u8[]>(512);
|
||||
memset(ffBuffer.get(), SAVE_FILL_VALUE, 512);
|
||||
|
||||
u32 offset = initialSize;
|
||||
// Align to 512 bytes
|
||||
if ((offset & 511) != 0)
|
||||
{
|
||||
u32 remainingTo512 = 512 - (offset & 511);
|
||||
UINT bytesWritten = 0;
|
||||
if (f_write(file.get(), ffBuffer.get(), remainingTo512, &bytesWritten) != FR_OK ||
|
||||
bytesWritten != remainingTo512)
|
||||
{
|
||||
LOG_FATAL("Failed to expand save file\n");
|
||||
return false;
|
||||
}
|
||||
offset += remainingTo512;
|
||||
}
|
||||
|
||||
// Write in 512-byte blocks
|
||||
while (offset < saveSize)
|
||||
{
|
||||
UINT bytesWritten = 0;
|
||||
if (f_write(file.get(), ffBuffer.get(), 512, &bytesWritten) != FR_OK ||
|
||||
bytesWritten != 512)
|
||||
{
|
||||
LOG_FATAL("Failed to expand save file\n");
|
||||
return false;
|
||||
}
|
||||
offset += 512;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD* clusterTab = (DWORD*)SHARED_SAVE_FILE_INFO->clusterMap;
|
||||
clusterTab[0] = sizeof(SHARED_SAVE_FILE_INFO->clusterMap) / sizeof(u32);
|
||||
file->cltbl = clusterTab;
|
||||
FRESULT seekResult = f_lseek(file.get(), CREATE_LINKMAP);
|
||||
if (seekResult != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to make save cluster table. Result: %d\n", seekResult);
|
||||
return false;
|
||||
}
|
||||
SHARED_SAVE_FILE_INFO->clusterShift = __builtin_ctz(file->obj.fs->csize);
|
||||
SHARED_SAVE_FILE_INFO->database = file->obj.fs->database;
|
||||
SHARED_SAVE_FILE_INFO->clusterMask = file->obj.fs->csize - 1;
|
||||
|
||||
LOG_DEBUG("Made save cluster table\n");
|
||||
|
||||
if (f_close(file.get()) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to close save file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
12
arm7/source/loader/CardSaveArranger.h
Normal file
12
arm7/source/loader/CardSaveArranger.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Class for setting up the save file for retail card roms.
|
||||
class CardSaveArranger
|
||||
{
|
||||
public:
|
||||
/// @brief Sets up the save file at \p savePath for a retail card rom with the given \p gameCode.
|
||||
/// @param gameCode The game code of the retail card rom.
|
||||
/// @param savePath The desired save file path.
|
||||
/// @return \c true when setting up the save was successful, or \c false otherwise.
|
||||
bool SetupCardSave(u32 gameCode, const TCHAR* savePath) const;
|
||||
};
|
||||
356
arm7/source/loader/DSMode.cpp
Normal file
356
arm7/source/loader/DSMode.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/i2c/i2cMcu.h>
|
||||
#include <libtwl/sound/twlI2s.h>
|
||||
#include <libtwl/sound/sound.h>
|
||||
#include <libtwl/spi/spiCodec.h>
|
||||
#include <libtwl/spi/spiPmic.h>
|
||||
#include <libtwl/sys/twlScfg.h>
|
||||
#include "ipc.h"
|
||||
#include "gameCode.h"
|
||||
#include "DSMode.h"
|
||||
|
||||
void DSMode::SwitchToDSMode(u32 gameCode) const
|
||||
{
|
||||
SwitchToDSTouchAndSoundMode(gameCode);
|
||||
mcu_writeReg(MCU_REG_MODE, 0);
|
||||
*(vu16*)0x04004C04 |= (1 << 8); // ntr wifi
|
||||
REG_SCFG_A9ROM = SCFG_A9ROM_DISABLE_SECURE | SCFG_A9ROM_NITRO;
|
||||
REG_SCFG_A7ROM = SCFG_A7ROM_DISABLE_SECURE | SCFG_A7ROM_NITRO | SCFG_A7ROM_DISABLE_FUSE;
|
||||
REG_SCFG_EXT = 0x12A03000u;
|
||||
sendToArm9(IPC_COMMAND_ARM9_SWITCH_TO_DS_MODE);
|
||||
receiveFromArm9();
|
||||
|
||||
LOG_DEBUG("Switched to ds mode\n");
|
||||
}
|
||||
|
||||
void DSMode::SwitchToDSTouchAndSoundMode(u32 gameCode) const
|
||||
{
|
||||
REG_I2SCNT = I2SCNT_MIX_RATIO_DSP_0_NITRO_8 | I2SCNT_FREQUENCY_32728_HZ;
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_NDAC_VAL, 0x87);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_NADC_VAL, 0x87);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_PLL_J, 21);
|
||||
}
|
||||
REG_I2SCNT |= I2SCNT_ENABLE;
|
||||
SwitchCodecToDSMode(gameCode);
|
||||
REG_SOUNDCNT = SOUNDCNT_MASTER_ENABLE | SOUNDCNT_MASTER_VOLUME(0x7F);
|
||||
|
||||
LOG_DEBUG("Switched to ds touch and sound mode\n");
|
||||
}
|
||||
|
||||
void DSMode::SwitchCodecToDSMode(u32 gameCode) const
|
||||
{
|
||||
// 0xAC: special setting (when found special gamecode)
|
||||
// 0xA7: normal setting (for any other gamecodes)
|
||||
u8 volLevel = ShouldUseVolumeFix(gameCode) ? 0xAC : 0xA7;
|
||||
|
||||
// Touchscreen
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_GPI3_PIN_CONTROL, 0x00);
|
||||
codec_readRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_3);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE3_SAR_ADC_CONTROL_1);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE0_DAC_DATA_PATH_SETUP);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE1_HPL_DRIVER);
|
||||
codec_readRegister(CODEC_REG_PAGE1_SPL_DRIVER);
|
||||
codec_readRegister(CODEC_REG_PAGE1_MICBIAS);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_VOLUME_CONTROL_FINE_ADJUST, 0x80);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_VOLUME_CONTROL, 0x0C);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_LEFT_ANALOG_VOL_TO_HPL, 0xFF);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_RIGHT_ANALOG_VOL_TO_HPR, 0xFF);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_LEFT_ANALOG_VOL_TO_SPL, 0x7F);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_RIGHT_ANALOG_VOL_TO_SPR, 0x7F);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HPL_DRIVER, 0x4A);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HPR_DRIVER, 0x4A);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_SPL_DRIVER, 0x10);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_SPR_DRIVER, 0x10);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC, 0x00);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_3);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE3_SAR_ADC_CONTROL_1);
|
||||
codec_writeRegister(CODEC_REG_PAGE3_SAR_ADC_CONTROL_1, 0x98);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_DAC_L_DAC_R_OUTPUT_MIXER_ROUTING, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HEADPHONE_DRIVERS, 0x14);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_CLASS_D_SPEAKER_AMPLIFIER, 0x14);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_DATA_PATH_SETUP, 0x00);
|
||||
codec_readRegister(CODEC_REG_PAGE0_DAC_NDAC_VAL);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_PLL_P_R, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_NDAC_VAL, 0x01);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_MDAC_VAL, 0x02);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_NADC_VAL, 0x01);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_MADC_VAL, 0x02);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_MICBIAS, 0x00);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_GPI3_PIN_CONTROL, 0x60);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_RESET, 0x01);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_GPI1_GPI2_PIN_CONTROL, 0x66);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE1_CLASS_D_SPEAKER_AMPLIFIER);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_CLASS_D_SPEAKER_AMPLIFIER, 0x10);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_CLOCK_GEN_MUXING, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_NADC_VAL, 0x81);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_MADC_VAL, 0x82);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC, 0x82);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_CLOCK_GEN_MUXING, 0x03);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_PLL_P_R, 0xA1);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_PLL_J, 0x15);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_NDAC_VAL, 0x87);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_MDAC_VAL, 0x83);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_NADC_VAL, 0x87);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_MADC_VAL, 0x83);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_3);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE3_SCAN_MODE_TIMER_CLOCK);
|
||||
codec_writeRegister(CODEC_REG_PAGE3_SCAN_MODE_TIMER_CLOCK, 0x08);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_4);
|
||||
{
|
||||
codec_writeRegister(0x08, 0x7F);
|
||||
codec_writeRegister(0x09, 0xE1);
|
||||
codec_writeRegister(0x0A, 0x80);
|
||||
codec_writeRegister(0x0B, 0x1F);
|
||||
codec_writeRegister(0x0C, 0x7F);
|
||||
codec_writeRegister(0x0D, 0xC1);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_LEFT_VOLUME_CONTROL, 0x08);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_RIGHT_VOLUME_CONTROL, 0x08);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_GPI3_PIN_CONTROL, 0x00);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_4);
|
||||
{
|
||||
codec_writeRegister(0x08, 0x7F);
|
||||
codec_writeRegister(0x09, 0xE1);
|
||||
codec_writeRegister(0x0A, 0x80);
|
||||
codec_writeRegister(0x0B, 0x1F);
|
||||
codec_writeRegister(0x0C, 0x7F);
|
||||
codec_writeRegister(0x0D, 0xC1);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_MIC_PGA, 0x2B);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_DELTA_SIGMA_MONO_ADC_CHANNEL_FINE_GAIN_INPUT_SELECTION_FOR_P_TERMINAL, 0x40);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_ADC_INPUT_SELECTION_FOR_M_TERMINAL, 0x40);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_INPUT_CM_SETTINGS, 0x60);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_SAR_ADC_VOLUME_CONTROL);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_SAR_ADC_VOLUME_CONTROL, 0x02);
|
||||
codec_readRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_SAR_ADC_VOLUME_CONTROL);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_SAR_ADC_VOLUME_CONTROL, 0x10);
|
||||
codec_readRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_SAR_ADC_VOLUME_CONTROL);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_SAR_ADC_VOLUME_CONTROL, 0x40);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HP_OUTPUT_DRIVERS_POP_REMOVAL_SETTINGS, 0x20);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_OUTPUT_DRIVER_PGA_RAMP_DOWN_PERIOD_CONTROL, 0xF0);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC);
|
||||
codec_readRegister(CODEC_REG_PAGE0_DAC_DATA_PATH_SETUP);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_DATA_PATH_SETUP, 0xD4);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_DAC_L_DAC_R_OUTPUT_MIXER_ROUTING, 0x44);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HEADPHONE_DRIVERS, 0xD4);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HPL_DRIVER, 0x4E);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HPR_DRIVER, 0x4E);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_LEFT_ANALOG_VOL_TO_HPL, 0x9E);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_RIGHT_ANALOG_VOL_TO_HPR, 0x9E);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_CLASS_D_SPEAKER_AMPLIFIER, 0xD4);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_SPL_DRIVER, 0x14);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_SPR_DRIVER, 0x14);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_LEFT_ANALOG_VOL_TO_SPL, 0xA7);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_RIGHT_ANALOG_VOL_TO_SPR, 0xA7);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_VOLUME_CONTROL, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_GPI3_PIN_CONTROL, 0x60);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_LEFT_ANALOG_VOL_TO_SPL, volLevel);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_RIGHT_ANALOG_VOL_TO_SPR, volLevel);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_MICBIAS, 0x03);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_3);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE3_SAR_ADC_CONTROL_2, 0x00);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HP_OUTPUT_DRIVERS_POP_REMOVAL_SETTINGS, 0x20);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_OUTPUT_DRIVER_PGA_RAMP_DOWN_PERIOD_CONTROL, 0xF0);
|
||||
codec_readRegister(CODEC_REG_PAGE1_OUTPUT_DRIVER_PGA_RAMP_DOWN_PERIOD_CONTROL);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_OUTPUT_DRIVER_PGA_RAMP_DOWN_PERIOD_CONTROL, 0x00);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_VOLUME_CONTROL_FINE_ADJUST, 0x80);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC, 0x00);
|
||||
|
||||
// Set remaining values
|
||||
codec_writeRegister(0x03, 0x44);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_DOSR_VAL_MSB, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_DOSR_VAL_LSB, 0x80);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_IDAC_VAL, 0x80);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_MINIDSP_INTERPOLATION, 0x08);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_AOSR_VAL, 0x80);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_IDAC_VAL, 0x80);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_MINIDSP_DECIMATION, 0x04);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_CLKOUT_M_VAL, 0x01);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BCLK_N_VAL, 0x01);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_FLAG_REGISTER, 0x80);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_GPIO1_IN_OUT_PIN_CONTROL, 0x34);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_GPIO2_IN_OUT_PIN_CONTROL, 0x32);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_SDOUT_OUT_PIN_CONTROL, 0x12);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_SDIN_IN_PIN_CONTROL, 0x03);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_MISO_OUT_PIN_CONTROL, 0x02);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_SCLK_IN_PIN_CONTROL, 0x03);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_INSTRUCTION_SET, 0x19);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_INSTRUCTION_SET, 0x05);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DRC_CONTROL_1, 0x0F);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DRC_CONTROL_2, 0x38);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BEEP_LENGTH_MSB, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BEEP_LENGTH_MID, 0x00);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BEEP_LENGTH_LSB, 0xEE);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BEEP_SIN_MSB, 0x10);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BEEP_SIN_LSB, 0xD8);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BEEP_COS_MSB, 0x7E);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_BEEP_COS_LSB, 0xE3);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_AGC_MAXIMUM_GAIN, 0x7F);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_SAR_ADC_VOLUME_CONTROL, 0xD2);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_VOL_MICDET_PIN_GAIN, 0x2C);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_OUTPUT_DRIVER_PGA_RAMP_DOWN_PERIOD_CONTROL, 0x70);
|
||||
codec_writeRegister(CODEC_REG_PAGE1_HP_DRIVER_CONTROL, 0x20);
|
||||
}
|
||||
|
||||
// Finish up!
|
||||
codec_setPage(CODEC_PAGE_3);
|
||||
{
|
||||
codec_readRegister(CODEC_REG_PAGE3_SAR_ADC_CONTROL_1);
|
||||
codec_writeRegister(CODEC_REG_PAGE3_SAR_ADC_CONTROL_1, 0x98);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_255);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE255_BACKWARDS_COMPATIBILITY_MODE, CODEC_PAGE255_BACKWARDS_COMPATIBILITY_MODE_ON);
|
||||
}
|
||||
|
||||
pmic_setAmplifierEnable(true);
|
||||
}
|
||||
|
||||
bool DSMode::ShouldUseVolumeFix(u32 gameCode) const
|
||||
{
|
||||
switch (gameCode & 0xFFFFFF)
|
||||
{
|
||||
case GAMECODE_NO_REGION("A3T"):
|
||||
case GAMECODE_NO_REGION("A4U"):
|
||||
case GAMECODE_NO_REGION("A5H"):
|
||||
case GAMECODE_NO_REGION("A5I"):
|
||||
case GAMECODE_NO_REGION("A8N"):
|
||||
case GAMECODE_NO_REGION("ABJ"):
|
||||
case GAMECODE_NO_REGION("ABN"):
|
||||
case GAMECODE_NO_REGION("ABX"):
|
||||
case GAMECODE_NO_REGION("ACC"):
|
||||
case GAMECODE_NO_REGION("ACL"):
|
||||
case GAMECODE_NO_REGION("ACZ"):
|
||||
case GAMECODE_NO_REGION("ADA"):
|
||||
case GAMECODE_NO_REGION("AHD"):
|
||||
case GAMECODE_NO_REGION("AJU"):
|
||||
case GAMECODE_NO_REGION("AKA"):
|
||||
case GAMECODE_NO_REGION("AKE"):
|
||||
case GAMECODE_NO_REGION("ALH"):
|
||||
case GAMECODE_NO_REGION("AMH"):
|
||||
case GAMECODE_NO_REGION("AN9"):
|
||||
case GAMECODE_NO_REGION("ANR"):
|
||||
case GAMECODE_NO_REGION("APA"):
|
||||
case GAMECODE_NO_REGION("APY"):
|
||||
case GAMECODE_NO_REGION("ART"):
|
||||
case GAMECODE_NO_REGION("AV2"):
|
||||
case GAMECODE_NO_REGION("AV3"):
|
||||
case GAMECODE_NO_REGION("AV4"):
|
||||
case GAMECODE_NO_REGION("AV5"):
|
||||
case GAMECODE_NO_REGION("AV6"):
|
||||
case GAMECODE_NO_REGION("AVI"):
|
||||
case GAMECODE_NO_REGION("AVT"):
|
||||
case GAMECODE_NO_REGION("AWH"):
|
||||
case GAMECODE_NO_REGION("AWY"):
|
||||
case GAMECODE_NO_REGION("AXB"):
|
||||
case GAMECODE_NO_REGION("AXJ"):
|
||||
case GAMECODE_NO_REGION("AY7"):
|
||||
case GAMECODE_NO_REGION("AYK"):
|
||||
case GAMECODE_NO_REGION("AZW"):
|
||||
case GAMECODE_NO_REGION("CPU"):
|
||||
case GAMECODE_NO_REGION("YB2"):
|
||||
case GAMECODE_NO_REGION("YB3"):
|
||||
case GAMECODE_NO_REGION("YBO"):
|
||||
case GAMECODE_NO_REGION("YCH"):
|
||||
case GAMECODE_NO_REGION("YCQ"):
|
||||
case GAMECODE_NO_REGION("YFE"):
|
||||
case GAMECODE_NO_REGION("YFS"):
|
||||
case GAMECODE_NO_REGION("YG8"):
|
||||
case GAMECODE_NO_REGION("YGD"):
|
||||
case GAMECODE_NO_REGION("YKR"):
|
||||
case GAMECODE_NO_REGION("YNZ"):
|
||||
case GAMECODE_NO_REGION("YO9"):
|
||||
case GAMECODE_NO_REGION("YON"):
|
||||
case GAMECODE_NO_REGION("YRM"):
|
||||
case GAMECODE_NO_REGION("YT3"):
|
||||
case GAMECODE_NO_REGION("YW2"):
|
||||
case GAMECODE_NO_REGION("YYK"):
|
||||
{
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
arm7/source/loader/DSMode.h
Normal file
18
arm7/source/loader/DSMode.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Class handling switching from DSi to DS mode.
|
||||
class DSMode
|
||||
{
|
||||
public:
|
||||
/// @brief Switches to DS mode for the game with the given \p gameCode.
|
||||
/// @param gameCode The game code of the game to switch to DS mode for.
|
||||
void SwitchToDSMode(u32 gameCode) const;
|
||||
|
||||
/// @brief Switches CODEC to DS mode for the game with the given \p gameCode.
|
||||
/// @param gameCode The game code of the game to switch CODEC to DS mode for.
|
||||
void SwitchToDSTouchAndSoundMode(u32 gameCode) const;
|
||||
|
||||
private:
|
||||
void SwitchCodecToDSMode(u32 gameCode) const;
|
||||
bool ShouldUseVolumeFix(u32 gameCode) const;
|
||||
};
|
||||
106
arm7/source/loader/DldiDriver.cpp
Normal file
106
arm7/source/loader/DldiDriver.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include "DldiDriver.h"
|
||||
|
||||
void DldiDriver::Relocate(u32 targetAddress)
|
||||
{
|
||||
u32 currentAddress = _driver->driverStartAddress;
|
||||
if (currentAddress == targetAddress)
|
||||
return;
|
||||
|
||||
u32 currentEndAddress = _driver->driverEndAddress;
|
||||
|
||||
if (_driver->fixFlags & DLDI_FIX_ALL)
|
||||
{
|
||||
LOG_WARNING("Dldi driver uses FIX_ALL flag");
|
||||
u32* ptr = (u32*)((u8*)_driver + sizeof(dldi_header_t));
|
||||
u32 size = currentEndAddress - currentAddress;
|
||||
for (u32 i = 0; i < size; i += 4)
|
||||
{
|
||||
u32 word = *ptr;
|
||||
if (currentAddress <= word && word < currentEndAddress)
|
||||
*ptr = word - currentAddress + targetAddress;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// note that we are not going to do those fixes when we did
|
||||
// FIX_ALL already, since it covers them already
|
||||
if (_driver->fixFlags & DLDI_FIX_GLUE)
|
||||
{
|
||||
u32* ptr = (u32*)((u8*)_driver + _driver->glueStartAddress - currentAddress);
|
||||
u32 size = _driver->glueEndAddress - _driver->glueStartAddress;
|
||||
for (u32 i = 0; i < size; i += 4)
|
||||
{
|
||||
u32 word = *ptr;
|
||||
if (currentAddress <= word && word < currentEndAddress)
|
||||
*ptr = word - currentAddress + targetAddress;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
if (_driver->fixFlags & DLDI_FIX_GOT)
|
||||
{
|
||||
u32* ptr = (u32*)((u8*)_driver + _driver->gotStartAddress - currentAddress);
|
||||
u32 size = _driver->gotEndAddress - _driver->gotStartAddress;
|
||||
for (u32 i = 0; i < size; i += 4)
|
||||
{
|
||||
u32 word = *ptr;
|
||||
if (currentAddress <= word && word < currentEndAddress)
|
||||
*ptr = word - currentAddress + targetAddress;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_driver->driverStartAddress = targetAddress;
|
||||
_driver->driverEndAddress = _driver->driverEndAddress + targetAddress - currentAddress;
|
||||
_driver->glueStartAddress = _driver->glueStartAddress + targetAddress - currentAddress;
|
||||
_driver->glueEndAddress = _driver->glueEndAddress + targetAddress - currentAddress;
|
||||
_driver->gotStartAddress = _driver->gotStartAddress + targetAddress - currentAddress;
|
||||
_driver->gotEndAddress = _driver->gotEndAddress + targetAddress - currentAddress;
|
||||
_driver->bssStartAddress = _driver->bssStartAddress + targetAddress - currentAddress;
|
||||
_driver->bssEndAddress = _driver->bssEndAddress + targetAddress - currentAddress;
|
||||
|
||||
_driver->startupFuncAddress = _driver->startupFuncAddress + targetAddress - currentAddress;
|
||||
_driver->isInsertedFuncAddress = _driver->isInsertedFuncAddress + targetAddress - currentAddress;
|
||||
_driver->readSectorsFuncAddress = _driver->readSectorsFuncAddress + targetAddress - currentAddress;
|
||||
_driver->writeSectorsFuncAddress = _driver->writeSectorsFuncAddress + targetAddress - currentAddress;
|
||||
_driver->clearStatusFuncAddress = _driver->clearStatusFuncAddress + targetAddress - currentAddress;
|
||||
_driver->shutdownFuncAddress = _driver->shutdownFuncAddress + targetAddress - currentAddress;
|
||||
}
|
||||
|
||||
void DldiDriver::PrepareForUse()
|
||||
{
|
||||
if (_driver->fixFlags & DLDI_FIX_BSS)
|
||||
{
|
||||
memset((u8*)_driver + _driver->bssStartAddress - _driver->driverStartAddress, 0,
|
||||
_driver->bssEndAddress - _driver->bssStartAddress);
|
||||
}
|
||||
}
|
||||
|
||||
bool DldiDriver::PatchTo(dldi_header_t* stub)
|
||||
{
|
||||
if (_driver->dldiMagic != DLDI_MAGIC)
|
||||
return false;
|
||||
|
||||
u32 stubSize = stub->stubSize;
|
||||
if (stubSize < _driver->driverSize)
|
||||
{
|
||||
LOG_ERROR("Dldi stub of size %d is not large enough for driver of size %d\n",
|
||||
stubSize, _driver->driverSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 targetAddress = stub->driverStartAddress;
|
||||
u32 driverSize = 1 << _driver->driverSize;
|
||||
|
||||
memcpy(stub, _driver, driverSize);
|
||||
stub->stubSize = stubSize;
|
||||
|
||||
auto newDriver = DldiDriver(stub);
|
||||
newDriver.Relocate(targetAddress);
|
||||
newDriver.PrepareForUse();
|
||||
|
||||
return true;
|
||||
}
|
||||
86
arm7/source/loader/DldiDriver.h
Normal file
86
arm7/source/loader/DldiDriver.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include "dldiHeader.h"
|
||||
|
||||
/// @brief Class representing a DLDI driver.
|
||||
class DldiDriver
|
||||
{
|
||||
dldi_header_t* _driver;
|
||||
public:
|
||||
explicit DldiDriver(dldi_header_t* driver)
|
||||
: _driver(driver) { }
|
||||
|
||||
/// @brief Returns whether the driver has been relocated to its current ram address.
|
||||
/// @return True if the driver is relocated to its current ram address, or false otherwise.
|
||||
bool IsRelocated() const
|
||||
{
|
||||
return _driver->driverStartAddress == (u32)_driver;
|
||||
}
|
||||
|
||||
/// @brief Relocates the driver to its current ram address.
|
||||
void Relocate()
|
||||
{
|
||||
Relocate((u32)_driver);
|
||||
}
|
||||
|
||||
/// @brief Relocates the driver to the given targetAddress.
|
||||
/// @param targetAddress The address to relocate the driver to.
|
||||
void Relocate(u32 targetAddress);
|
||||
|
||||
/// @brief Clears the bss area of the driver if needed, making it ready for use.
|
||||
/// This method should only be used after placing the driver in a memory region
|
||||
/// with enough space and relocating it.
|
||||
void PrepareForUse();
|
||||
|
||||
/// @brief Patches this dldi driver into the given stub.
|
||||
/// @param stub The stub to patch the dldi driver into.
|
||||
/// @return \c true if patching was successful, or \c false otherwise.
|
||||
bool PatchTo(dldi_header_t* stub);
|
||||
|
||||
/// @brief Calls the startup function of the DLDI driver and returns the result.
|
||||
/// @return \c true if startup was successful, or \c false otherwise.
|
||||
bool Startup()
|
||||
{
|
||||
return ((dldi_startup_func_t)_driver->startupFuncAddress)();
|
||||
}
|
||||
|
||||
/// @brief Calls the is inserted function of the DLDI driver and returns the result.
|
||||
/// @return \c true if the storage medium is inserted, or \c false otherwise.
|
||||
bool IsInserted()
|
||||
{
|
||||
return ((dldi_inserted_func_t)_driver->isInsertedFuncAddress)();
|
||||
}
|
||||
|
||||
/// @brief Reads sectors using this DLDI driver.
|
||||
/// @param sector The sector to start reading at.
|
||||
/// @param count The number of sectors to read.
|
||||
/// @param dst The destination buffer.
|
||||
/// @return \c true if reading was successful, or \c false otherwise.
|
||||
bool ReadSectors(u32 sector, u32 count, void* dst)
|
||||
{
|
||||
return ((dldi_read_func_t)_driver->readSectorsFuncAddress)(sector, count, dst);
|
||||
}
|
||||
|
||||
/// @brief Writes sectors using this DLDI driver.
|
||||
/// @param sector The sector to start writing at.
|
||||
/// @param count The number of sectors to write.
|
||||
/// @param src The source buffer.
|
||||
/// @return \c true if writing was successful, or \c false otherwise.
|
||||
bool WriteSectors(u32 sector, u32 count, const void* src)
|
||||
{
|
||||
return ((dldi_write_func_t)_driver->writeSectorsFuncAddress)(sector, count, src);
|
||||
}
|
||||
|
||||
/// @brief Calls the clear status function of the DLDI driver and returns the result.
|
||||
/// @return \c true if clear status was successful, or \c false otherwise.
|
||||
bool ClearStatus()
|
||||
{
|
||||
return ((dldi_clearstatus_func_t)_driver->clearStatusFuncAddress)();
|
||||
}
|
||||
|
||||
/// @brief Calls the shutdown function of the DLDI driver and returns the result.
|
||||
/// @return \c true if shutdown was successful, or \c false otherwise.
|
||||
bool Shutdown()
|
||||
{
|
||||
return ((dldi_shutdown_func_t)_driver->shutdownFuncAddress)();
|
||||
}
|
||||
};
|
||||
39
arm7/source/loader/DsiDeviceList.h
Normal file
39
arm7/source/loader/DsiDeviceList.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_DRIVE_SDMC 0
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_DRIVE_NAND 1
|
||||
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_TYPE_PHYSICAL (0 << 3)
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_TYPE_FILE (1 << 3)
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_TYPE_FOLDER (2 << 3)
|
||||
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_PARTITION_FIRST (0 << 5)
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_PARTITION_SECOND (1 << 5)
|
||||
|
||||
#define DSI_DEVICELIST_ENTRY_FLAGS_ENCRYPTED (1 << 7)
|
||||
|
||||
#define DSI_DEVICELIST_ENTRY_ACCESS_RIGHTS_READ (1 << 1)
|
||||
#define DSI_DEVICELIST_ENTRY_ACCESS_RIGHTS_WRITE (1 << 2)
|
||||
|
||||
/// @brief Struct representing a DSi device list entry.
|
||||
struct dsi_devicelist_entry_t
|
||||
{
|
||||
char driveLetter;
|
||||
u8 flags;
|
||||
u8 accessRights;
|
||||
u8 padding;
|
||||
char deviceName[16];
|
||||
char path[64];
|
||||
};
|
||||
|
||||
static_assert(sizeof(dsi_devicelist_entry_t) == 0x54, "Invalid dsi_devicelist_entry_t size.");
|
||||
|
||||
/// @brief Struct representing a DSi device list.
|
||||
struct dsi_devicelist_t
|
||||
{
|
||||
dsi_devicelist_entry_t deviceList[11];
|
||||
u8 padding[0x24];
|
||||
char appFileName[64];
|
||||
};
|
||||
|
||||
static_assert(sizeof(dsi_devicelist_t) == 0x400, "Invalid dsi_devicelist_t size.");
|
||||
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;
|
||||
}
|
||||
65
arm7/source/loader/DsiWareSaveArranger.h
Normal file
65
arm7/source/loader/DsiWareSaveArranger.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "fat/ff.h"
|
||||
#include "ndsHeader.h"
|
||||
|
||||
/// @brief Struct holding the result of setting up DSiWare save data.
|
||||
struct DsiWareSaveResult
|
||||
{
|
||||
/// @brief The short filename path of the private save file.
|
||||
char privateSavePath[64];
|
||||
|
||||
/// @brief The short filename path of the public save file.
|
||||
char publicSavePath[64];
|
||||
|
||||
/// @brief The short filename path of rom file.
|
||||
char romFilePath[64];
|
||||
};
|
||||
|
||||
/// @brief Class for setting up the save files for DSiWare roms.
|
||||
class DsiWareSaveArranger
|
||||
{
|
||||
public:
|
||||
/// @brief Sets up the save files for a DSiWare rom with the given \p romPath and \p romHeader.
|
||||
/// @param romPath The path to the DSiWare rom to setup the save files for.
|
||||
/// @param romHeader The header of the DSiWare rom.
|
||||
/// @param result Struct in which the resulting paths are returned.
|
||||
/// @return \c true when setting up the save was successful, or \c false otherwise.
|
||||
bool SetupDsiWareSave(const TCHAR* romPath, const nds_header_twl_t& romHeader, DsiWareSaveResult& result) const;
|
||||
|
||||
private:
|
||||
#pragma pack(push, 1)
|
||||
/// @brief Struct representing a FAT header.
|
||||
struct fat_header_t
|
||||
{
|
||||
u8 jumpInstruction[3];
|
||||
u8 oemName[8];
|
||||
u16 bytesPerSector;
|
||||
u8 sectorsPerCluster;
|
||||
u16 reservedSectors;
|
||||
u8 numberOfFats;
|
||||
u16 rootEntries;
|
||||
u16 totalSectorsSmall;
|
||||
u8 mediaType;
|
||||
u16 fatSectorCount;
|
||||
u16 sectorsPerTrack;
|
||||
u16 numberOfHeads;
|
||||
u32 hiddenSectors;
|
||||
u32 totalSectorsLarge;
|
||||
u8 diskNumber;
|
||||
u8 BS_Reserved1;
|
||||
u8 bootSignature;
|
||||
u32 volumeSerialNumber;
|
||||
u8 volumeLabel[11];
|
||||
u8 fileSystemType[8];
|
||||
u8 bootCode[448];
|
||||
u16 endOfSectorMarker;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(fat_header_t) == 512, "Invalid size of fat_header_t.");
|
||||
|
||||
bool SetupDsiWareSaveFile(const TCHAR* savePath, u32 saveSize) const;
|
||||
std::unique_ptr<fat_header_t> CreateFatHeader(u32 saveSize) const;
|
||||
bool CreateDeviceListPath(TCHAR* savePath, char* deviceListPath) const;
|
||||
};
|
||||
39
arm7/source/loader/InverseKmpMatcher.h
Normal file
39
arm7/source/loader/InverseKmpMatcher.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
template <typename T, u32 PatternLength>
|
||||
class InverseKmpMatcher
|
||||
{
|
||||
const T* _pattern;
|
||||
u8 _prefixFunc[PatternLength];
|
||||
public:
|
||||
consteval InverseKmpMatcher(const T(&pattern)[PatternLength])
|
||||
: _pattern(pattern)
|
||||
{
|
||||
_prefixFunc[0] = 0;
|
||||
int k = 0;
|
||||
for (u32 q = 1; q < PatternLength; q++)
|
||||
{
|
||||
while (k > 0 && pattern[k] != pattern[q])
|
||||
k = _prefixFunc[k - 1];
|
||||
if (pattern[k] == pattern[q])
|
||||
k++;
|
||||
_prefixFunc[q] = k;
|
||||
}
|
||||
}
|
||||
|
||||
int FindFirstOccurance(const T* data, u32 length) const
|
||||
{
|
||||
int q = 0;
|
||||
for (u32 i = 0; i < length; i++)
|
||||
{
|
||||
while (q > 0 && ~_pattern[q] != data[i])
|
||||
q = _prefixFunc[q - 1];
|
||||
if (~_pattern[q] == data[i])
|
||||
q++;
|
||||
if (q == PatternLength)
|
||||
return i - q + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
1007
arm7/source/loader/NdsLoader.cpp
Normal file
1007
arm7/source/loader/NdsLoader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
75
arm7/source/loader/NdsLoader.h
Normal file
75
arm7/source/loader/NdsLoader.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "ndsHeader.h"
|
||||
#include "DsiWareSaveArranger.h"
|
||||
#include "BootMode.h"
|
||||
|
||||
struct dsi_devicelist_entry_t;
|
||||
|
||||
/// @brief Class for loading DS(i) roms.
|
||||
class NdsLoader
|
||||
{
|
||||
public:
|
||||
/// @brief Sets the \p romPath.
|
||||
/// @param romPath The rom path.
|
||||
void SetRomPath(const TCHAR* romPath)
|
||||
{
|
||||
_romPath = romPath;
|
||||
}
|
||||
|
||||
/// @brief Sets the \p savePath to use.
|
||||
/// @param savePath The save path to use.
|
||||
void SetSavePath(const TCHAR* savePath)
|
||||
{
|
||||
_savePath = savePath;
|
||||
}
|
||||
|
||||
/// @brief Sets the argv arguments to pass to the rom.
|
||||
/// @param arguments The argv arguments.
|
||||
/// @param argumentsLength The length of the argv arguments.
|
||||
void SetArguments(const char* arguments, u32 argumentsLength)
|
||||
{
|
||||
_arguments = arguments;
|
||||
_argumentsLength = argumentsLength;
|
||||
}
|
||||
|
||||
/// @brief Loads the rom according to the specified \p bootMode.
|
||||
/// @param bootMode The boot mode.
|
||||
void Load(BootMode bootMode);
|
||||
|
||||
private:
|
||||
FIL _romFile;
|
||||
const TCHAR* _romPath;
|
||||
const TCHAR* _savePath;
|
||||
u32 _argumentsLength = 0;
|
||||
const char* _arguments = nullptr;
|
||||
nds_header_twl_t _romHeader;
|
||||
DsiWareSaveResult _dsiwareSaveResult;
|
||||
|
||||
bool IsCloneBootRom(u32 romOffset);
|
||||
void ApplyArm7Patches();
|
||||
void SetupSharedMemory(u32 cardId, u32 agbMem, u32 resetParam, u32 romOffset, u32 bootType);
|
||||
void LoadFirmwareUserSettings();
|
||||
bool ShouldAttemptDldiPatch();
|
||||
void ClearMainMemory();
|
||||
void CreateRomClusterTable();
|
||||
bool TryLoadRomHeader(u32 romOffset);
|
||||
void HandleCardSave();
|
||||
void HandleAntiPiracy();
|
||||
void RemapWram();
|
||||
bool TryLoadArm9();
|
||||
bool TryLoadArm9i();
|
||||
bool TryDecryptArm9i();
|
||||
bool TryDecryptArm7i();
|
||||
bool TryLoadArm7();
|
||||
bool TryLoadArm7i();
|
||||
void HandleDldiPatching();
|
||||
void StartRom(BootMode bootMode);
|
||||
void SetupTwlConfig();
|
||||
void SetDeviceListEntry(dsi_devicelist_entry_t& deviceListEntry,
|
||||
char driveLetter, const char* deviceName, const char* path, u8 flags, u8 accessRights);
|
||||
void SetupDsiDeviceList();
|
||||
void InsertArgv();
|
||||
bool TrySetupDsiWareSave();
|
||||
bool TryDecryptSecureArea();
|
||||
};
|
||||
60
arm7/source/loader/PicoLoaderArranger.cpp
Normal file
60
arm7/source/loader/PicoLoaderArranger.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "common.h"
|
||||
#include <memory>
|
||||
#include "PicoLoaderArranger.h"
|
||||
|
||||
#define PICO_LOADER_9_PATH "/_pico/picoLoader9.bin"
|
||||
#define PICO_LOADER_7_PATH "/_pico/picoLoader7.bin"
|
||||
|
||||
bool PicoLoaderArranger::SetupPicoLoaderInfo(loader_info_t* info) const
|
||||
{
|
||||
auto file = std::make_unique<FIL>();
|
||||
if (f_open(file.get(), PICO_LOADER_9_PATH, FA_OPEN_EXISTING | FA_READ) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to open picoLoader9.bin\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD* clusterTab = (DWORD*)info->clusterMap9;
|
||||
clusterTab[0] = sizeof(info->clusterMap9) / sizeof(u32);
|
||||
file->cltbl = clusterTab;
|
||||
FRESULT seekResult = f_lseek(file.get(), CREATE_LINKMAP);
|
||||
if (seekResult != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to make picoLoader9 cluster table. Result: %d\n", seekResult);
|
||||
return false;
|
||||
}
|
||||
|
||||
info->clusterShift = __builtin_ctz(file->obj.fs->csize);
|
||||
info->picoLoaderBootDrive = gLoaderHeader.bootDrive;
|
||||
info->database = file->obj.fs->database;
|
||||
|
||||
if (f_close(file.get()) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to close picoLoader9 file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f_open(file.get(), PICO_LOADER_7_PATH, FA_OPEN_EXISTING | FA_READ) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to open picoLoader7.bin\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
clusterTab = (DWORD*)info->clusterMap7;
|
||||
clusterTab[0] = sizeof(info->clusterMap7) / sizeof(u32);
|
||||
file->cltbl = clusterTab;
|
||||
seekResult = f_lseek(file.get(), CREATE_LINKMAP);
|
||||
if (seekResult != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to make picoLoader7 cluster table. Result: %d\n", seekResult);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f_close(file.get()) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to close picoLoader7 file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
12
arm7/source/loader/PicoLoaderArranger.h
Normal file
12
arm7/source/loader/PicoLoaderArranger.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "LoaderInfo.h"
|
||||
|
||||
/// @brief Class for setting up \see loader_info_t.
|
||||
class PicoLoaderArranger
|
||||
{
|
||||
public:
|
||||
/// @brief Sets up the given \p info for being able to reload Pico Loader at a later time.
|
||||
/// @param info The \see loader_info_t struct to fill.
|
||||
/// @return \c true when setting up was successful, or \c false otherwise.
|
||||
bool SetupPicoLoaderInfo(loader_info_t* info) const;
|
||||
};
|
||||
22
arm7/source/loader/SaveList.cpp
Normal file
22
arm7/source/loader/SaveList.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "common.h"
|
||||
#include <algorithm>
|
||||
#include "SaveList.h"
|
||||
|
||||
const SaveListEntry* SaveList::FindEntry(u32 gameCode)
|
||||
{
|
||||
if (_count != 0)
|
||||
{
|
||||
const auto gameEntry = std::lower_bound(_entries.get(), _entries.get() + _count, gameCode,
|
||||
[] (const SaveListEntry& entry, u32 value)
|
||||
{
|
||||
return entry.GetGameCode() < value;
|
||||
});
|
||||
|
||||
if (gameEntry != _entries.get() + _count && gameEntry->GetGameCode() == gameCode)
|
||||
{
|
||||
return gameEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
74
arm7/source/loader/SaveList.h
Normal file
74
arm7/source/loader/SaveList.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "common.h"
|
||||
#include "CardSaveType.h"
|
||||
|
||||
/// @brief Class representing a save list entry.
|
||||
class SaveListEntry
|
||||
{
|
||||
u32 gameCode;
|
||||
u8 saveType; // see CardSaveType
|
||||
u8 saveSize; // 0 or 1 << x
|
||||
u8 reserved[2]; // for possible future use
|
||||
|
||||
public:
|
||||
u32 GetGameCode() const { return gameCode; }
|
||||
CardSaveType GetSaveType() const { return static_cast<CardSaveType>(saveType); }
|
||||
u32 GetSaveSize() const { return saveSize == 0 ? 0 : (1u << saveSize); }
|
||||
|
||||
void Dump() const
|
||||
{
|
||||
const char* saveType;
|
||||
switch (GetSaveType())
|
||||
{
|
||||
case CardSaveType::None:
|
||||
{
|
||||
saveType = "none";
|
||||
break;
|
||||
}
|
||||
case CardSaveType::Eeprom:
|
||||
{
|
||||
saveType = "eeprom";
|
||||
break;
|
||||
}
|
||||
case CardSaveType::Flash:
|
||||
{
|
||||
saveType = "flash";
|
||||
break;
|
||||
}
|
||||
case CardSaveType::Nand:
|
||||
{
|
||||
saveType = "nand";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
saveType = "unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("%c%c%c%c - %s - 0x%X\n",
|
||||
gameCode & 0xFF, (gameCode >> 8) & 0xFF, (gameCode >> 16) & 0xFF, gameCode >> 24,
|
||||
saveType,
|
||||
GetSaveSize());
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SaveListEntry) == 8, "Invalid sizeof(SaveListEntry)");
|
||||
|
||||
/// @brief Class representing a save list.
|
||||
class SaveList
|
||||
{
|
||||
public:
|
||||
SaveList(std::unique_ptr<const SaveListEntry[]> entries, u32 count)
|
||||
: _entries(std::move(entries)), _count(count) { }
|
||||
|
||||
/// @brief Attempts to find the save list entry for the given \p gameCode.
|
||||
/// @param gameCode The game code to search for.
|
||||
/// @return A pointer to the \see SaveListEntry when found, or \c nullptr otherwise.
|
||||
const SaveListEntry* FindEntry(u32 gameCode);
|
||||
|
||||
private:
|
||||
std::unique_ptr<const SaveListEntry[]> _entries;
|
||||
u32 _count;
|
||||
};
|
||||
24
arm7/source/loader/SaveListFactory.cpp
Normal file
24
arm7/source/loader/SaveListFactory.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "common.h"
|
||||
#include "SaveListFactory.h"
|
||||
|
||||
SaveList* SaveListFactory::CreateFromFile(const TCHAR *path)
|
||||
{
|
||||
FIL file;
|
||||
if (f_open(&file, path, FA_OPEN_EXISTING | FA_READ) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to open save list file\n");
|
||||
return nullptr;
|
||||
}
|
||||
const u32 entryCount = f_size(&file) / sizeof(SaveListEntry);
|
||||
auto entries = std::make_unique_for_overwrite<SaveListEntry[]>(entryCount);
|
||||
UINT bytesRead = 0;
|
||||
FRESULT result = f_read(&file, entries.get(), entryCount * sizeof(SaveListEntry), &bytesRead);
|
||||
if (result != FR_OK || bytesRead != entryCount * sizeof(SaveListEntry))
|
||||
{
|
||||
LOG_FATAL("Failed to read save list file\n");
|
||||
return nullptr;
|
||||
}
|
||||
f_close(&file);
|
||||
|
||||
return new SaveList(std::move(entries), entryCount);
|
||||
}
|
||||
12
arm7/source/loader/SaveListFactory.h
Normal file
12
arm7/source/loader/SaveListFactory.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "SaveList.h"
|
||||
|
||||
/// @brief Factory for creating \see SaveList instances.
|
||||
class SaveListFactory
|
||||
{
|
||||
public:
|
||||
/// @brief Creates a \see SaveList instance from the file at the given \p path.
|
||||
/// @param path The save list file path.
|
||||
/// @return A pointer to the constructed \see SaveList instance, or \c nullptr if construction failed.
|
||||
SaveList* CreateFromFile(const TCHAR* path);
|
||||
};
|
||||
104
arm7/source/loader/SdmmcDefinitions.h
Normal file
104
arm7/source/loader/SdmmcDefinitions.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <nds/ndstypes.h>
|
||||
|
||||
#define DATA32_SUPPORT
|
||||
|
||||
#define SDMMC_BASE 0x04004800
|
||||
|
||||
|
||||
#define REG_SDCMD 0x00
|
||||
#define REG_SDPORTSEL 0x02
|
||||
#define REG_SDCMDARG 0x04
|
||||
#define REG_SDCMDARG0 0x04
|
||||
#define REG_SDCMDARG1 0x06
|
||||
#define REG_SDSTOP 0x08
|
||||
#define REG_SDRESP 0x0c
|
||||
#define REG_SDBLKCOUNT 0x0a
|
||||
|
||||
#define REG_SDRESP0 0x0c
|
||||
#define REG_SDRESP1 0x0e
|
||||
#define REG_SDRESP2 0x10
|
||||
#define REG_SDRESP3 0x12
|
||||
#define REG_SDRESP4 0x14
|
||||
#define REG_SDRESP5 0x16
|
||||
#define REG_SDRESP6 0x18
|
||||
#define REG_SDRESP7 0x1a
|
||||
|
||||
#define REG_SDSTATUS0 0x1c
|
||||
#define REG_SDSTATUS1 0x1e
|
||||
|
||||
#define REG_SDIRMASK0 0x20
|
||||
#define REG_SDIRMASK1 0x22
|
||||
#define REG_SDCLKCTL 0x24
|
||||
|
||||
#define REG_SDBLKLEN 0x26
|
||||
#define REG_SDOPT 0x28
|
||||
#define REG_SDFIFO 0x30
|
||||
|
||||
#define REG_SDDATACTL 0xd8
|
||||
#define REG_SDRESET 0xe0
|
||||
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
|
||||
|
||||
#define REG_SDDATACTL32 0x100
|
||||
#define REG_SDBLKLEN32 0x104
|
||||
#define REG_SDBLKCOUNT32 0x108
|
||||
#define REG_SDFIFO32 0x10C
|
||||
|
||||
#define REG_CLK_AND_WAIT_CTL 0x138
|
||||
#define REG_RESET_SDIO 0x1e0
|
||||
//The below defines are from linux kernel drivers/mmc tmio_mmc.h.
|
||||
/* Definitions for values the CTRL_STATUS register can take. */
|
||||
#define TMIO_STAT0_CMDRESPEND 0x0001
|
||||
#define TMIO_STAT0_DATAEND 0x0004
|
||||
#define TMIO_STAT0_CARD_REMOVE 0x0008
|
||||
#define TMIO_STAT0_CARD_INSERT 0x0010
|
||||
#define TMIO_STAT0_SIGSTATE 0x0020
|
||||
#define TMIO_STAT0_WRPROTECT 0x0080
|
||||
#define TMIO_STAT0_CARD_REMOVE_A 0x0100
|
||||
#define TMIO_STAT0_CARD_INSERT_A 0x0200
|
||||
#define TMIO_STAT0_SIGSTATE_A 0x0400
|
||||
|
||||
#define TMIO_STAT1_CMD_IDX_ERR 0x0001
|
||||
#define TMIO_STAT1_CRCFAIL 0x0002
|
||||
#define TMIO_STAT1_STOPBIT_ERR 0x0004
|
||||
#define TMIO_STAT1_DATATIMEOUT 0x0008
|
||||
#define TMIO_STAT1_RXOVERFLOW 0x0010
|
||||
#define TMIO_STAT1_TXUNDERRUN 0x0020
|
||||
#define TMIO_STAT1_CMDTIMEOUT 0x0040
|
||||
#define TMIO_STAT1_RXRDY 0x0100
|
||||
#define TMIO_STAT1_TXRQ 0x0200
|
||||
#define TMIO_STAT1_ILL_FUNC 0x2000
|
||||
#define TMIO_STAT1_CMD_BUSY 0x4000
|
||||
#define TMIO_STAT1_ILL_ACCESS 0x8000
|
||||
|
||||
#define SDMC_NORMAL 0x00000000
|
||||
#define SDMC_ERR_COMMAND 0x00000001
|
||||
#define SDMC_ERR_CRC 0x00000002
|
||||
#define SDMC_ERR_END 0x00000004
|
||||
#define SDMC_ERR_TIMEOUT 0x00000008
|
||||
#define SDMC_ERR_FIFO_OVF 0x00000010
|
||||
#define SDMC_ERR_FIFO_UDF 0x00000020
|
||||
#define SDMC_ERR_WP 0x00000040
|
||||
#define SDMC_ERR_ABORT 0x00000080
|
||||
#define SDMC_ERR_FPGA_TIMEOUT 0x00000100
|
||||
#define SDMC_ERR_PARAM 0x00000200
|
||||
#define SDMC_ERR_R1_STATUS 0x00000800
|
||||
#define SDMC_ERR_NUM_WR_SECTORS 0x00001000
|
||||
#define SDMC_ERR_RESET 0x00002000
|
||||
#define SDMC_ERR_ILA 0x00004000
|
||||
#define SDMC_ERR_INFO_DETECT 0x00008000
|
||||
|
||||
#define SDMC_STAT_ERR_UNKNOWN 0x00080000
|
||||
#define SDMC_STAT_ERR_CC 0x00100000
|
||||
#define SDMC_STAT_ERR_ECC_FAILED 0x00200000
|
||||
#define SDMC_STAT_ERR_CRC 0x00800000
|
||||
#define SDMC_STAT_ERR_OTHER 0xf9c70008
|
||||
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
|
||||
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
|
||||
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
|
||||
|
||||
#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND)
|
||||
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
|
||||
153
arm7/source/loader/TwlAes.cpp
Normal file
153
arm7/source/loader/TwlAes.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/sys/twlScfg.h>
|
||||
#include <libtwl/sys/twlFuse.h>
|
||||
#include <libtwl/dma/dmaTwl.h>
|
||||
#include "TwlAes.h"
|
||||
|
||||
#define KEY_SLOT_MODULE 0
|
||||
#define KEY_SLOT_NAND 3
|
||||
|
||||
#define MODULE_KEYX_NINT 0x746E694E // Nint
|
||||
#define MODULE_KEYX_ENDO 0x6F646E65 // endo
|
||||
|
||||
#define NAND_KEYX_DSI_XOR_LO 0x24EE6906
|
||||
#define NAND_KEYX_DSI_XOR_HI 0xE65B601D
|
||||
|
||||
#define NAND_KEYX_3DS_NINT 0x544E494E // NINT
|
||||
#define NAND_KEYX_3DS_ENDO 0x4F444E45 // ENDO
|
||||
|
||||
#define NAND_KEYY_WORD_3 0xE1A00005 // mov r0, r5
|
||||
|
||||
#define DMA_CHANNEL_AES_OUT 0
|
||||
#define DMA_CHANNEL_AES_IN 1
|
||||
|
||||
void TwlAes::SetupAes(const nds_header_twl_t* romHeader) const
|
||||
{
|
||||
// ensure AES is enabled
|
||||
REG_SCFG_EXT |= SCFG_EXT_AES;
|
||||
REG_SCFG_CLK |= SCFG_CLK_AES;
|
||||
|
||||
REG_AES_CNT = 0;
|
||||
|
||||
aes_reset();
|
||||
aes_reset();
|
||||
aes_waitKeyBusy();
|
||||
|
||||
SetupModuleKeyXY(romHeader);
|
||||
SetupNandKeyX();
|
||||
aes_waitKeyBusy();
|
||||
(®_AES_SEED0)[KEY_SLOT_NAND * 3].words[3] = NAND_KEYY_WORD_3;
|
||||
}
|
||||
|
||||
void TwlAes::DecryptModuleAes(void* data, u32 length, const aes_u128_t* iv) const
|
||||
{
|
||||
REG_AES_CNT = 0;
|
||||
|
||||
aes_reset();
|
||||
aes_reset();
|
||||
aes_waitKeyBusy();
|
||||
|
||||
aes_setKeySlot(0);
|
||||
aes_waitKeyBusy();
|
||||
|
||||
u32 offset = 0;
|
||||
while (length > 0)
|
||||
{
|
||||
REG_AES_CNT = 0;
|
||||
aes_reset();
|
||||
u32 blockLength = std::min<u32>(length, 0xFFFF0u);
|
||||
|
||||
auto ctr = *iv;
|
||||
((u64*)&ctr)[1] += __builtin_add_overflow(((u64*)&ctr)[0], offset >> 4, &((u64*)&ctr)[0]);
|
||||
aes_setInitializationVector(&ctr);
|
||||
aes_setPayloadBlockCount(blockLength >> 4);
|
||||
|
||||
LOG_DEBUG("%p\n", (u8*)data + offset);
|
||||
|
||||
dma_twl_config_t inputDmaConfig
|
||||
{
|
||||
.src = (u8*)data + offset,
|
||||
.dst = (void*)®_AES_IFIFO,
|
||||
.totalWordCount = blockLength >> 2,
|
||||
.wordCount = 4,
|
||||
.blockInterval = NDMABCNT_INTERVAL(8),
|
||||
.fillData = 0,
|
||||
.control = NDMACNT_DST_MODE_FIXED | NDMACNT_SRC_MODE_INCREMENT |
|
||||
NDMACNT_PHYSICAL_COUNT_4 | NDMACNT_MODE_AES_IN | NDMACNT_ENABLE
|
||||
};
|
||||
dma_twlSetParams(DMA_CHANNEL_AES_IN, &inputDmaConfig);
|
||||
|
||||
dma_twl_config_t outputDmaConfig
|
||||
{
|
||||
.src = (const void*)®_AES_OFIFO,
|
||||
.dst = (u8*)data + offset,
|
||||
.totalWordCount = blockLength >> 2,
|
||||
.wordCount = 4,
|
||||
.blockInterval = NDMABCNT_INTERVAL(8),
|
||||
.fillData = 0,
|
||||
.control = NDMACNT_SRC_MODE_FIXED | NDMACNT_DST_MODE_INCREMENT |
|
||||
NDMACNT_PHYSICAL_COUNT_4 | NDMACNT_MODE_AES_OUT | NDMACNT_ENABLE
|
||||
};
|
||||
dma_twlSetParams(DMA_CHANNEL_AES_OUT, &outputDmaConfig);
|
||||
|
||||
aes_start(
|
||||
AES_CNT_INPUT_FIFO_DMA_SIZE_4_BYTES |
|
||||
AES_CNT_OUTPUT_FIFO_DMA_SIZE_4_BYTES |
|
||||
AES_CNT_MODE_CTR);
|
||||
dma_twlWait(DMA_CHANNEL_AES_OUT);
|
||||
aes_waitBusy();
|
||||
|
||||
offset += blockLength;
|
||||
length -= blockLength;
|
||||
}
|
||||
}
|
||||
|
||||
void TwlAes::SetupModuleKeyXY(const nds_header_twl_t* romHeader) const
|
||||
{
|
||||
if ((romHeader->ntrHeader.twlFlags & (1 << 2)) || (romHeader->twlFlags2 & (1 << 7)))
|
||||
{
|
||||
// debug
|
||||
aes_setKey(KEY_SLOT_MODULE, (const aes_u128_t*)romHeader);
|
||||
}
|
||||
else
|
||||
{
|
||||
// retail
|
||||
aes_u128_t keyX { .words =
|
||||
{
|
||||
MODULE_KEYX_NINT,
|
||||
MODULE_KEYX_ENDO,
|
||||
romHeader->ntrHeader.gameCode,
|
||||
__builtin_bswap32(romHeader->ntrHeader.gameCode)
|
||||
}};
|
||||
aes_setKeyXY(KEY_SLOT_MODULE, &keyX, (const aes_u128_t*)romHeader->arm9iSha1Hmac);
|
||||
}
|
||||
}
|
||||
|
||||
void TwlAes::SetupNandKeyX() const
|
||||
{
|
||||
if ((REG_SCFG_A7ROM & SCFG_A7ROM_DISABLE_FUSE) || (REG_FUSE_VERIFY & FUSE_VERIFY_ERROR))
|
||||
{
|
||||
// No access to the console id register. We'll assume nand key x is already setup.
|
||||
return;
|
||||
}
|
||||
|
||||
u32 consoleIdLo = REG_FUSE_ID0;
|
||||
u32 consoleIdHi = REG_FUSE_ID1;
|
||||
|
||||
aes_u128_t keyX;
|
||||
keyX.words[0] = consoleIdLo;
|
||||
if (consoleIdLo & 0x80000000)
|
||||
{
|
||||
// 3DS
|
||||
keyX.words[1] = NAND_KEYX_3DS_NINT;
|
||||
keyX.words[2] = NAND_KEYX_3DS_ENDO;
|
||||
}
|
||||
else
|
||||
{
|
||||
// DSi
|
||||
keyX.words[1] = consoleIdLo ^ NAND_KEYX_DSI_XOR_LO;
|
||||
keyX.words[2] = consoleIdHi ^ NAND_KEYX_DSI_XOR_HI;
|
||||
}
|
||||
keyX.words[3] = consoleIdHi;
|
||||
aes_setKeyX(KEY_SLOT_NAND, &keyX);
|
||||
}
|
||||
22
arm7/source/loader/TwlAes.h
Normal file
22
arm7/source/loader/TwlAes.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <libtwl/aes/aes.h>
|
||||
#include "ndsHeader.h"
|
||||
|
||||
/// @brief Class handling AES crypto for DSi roms.
|
||||
class TwlAes
|
||||
{
|
||||
public:
|
||||
/// @brief Sets up AES for the DSi rom with the given \p romHeader.
|
||||
/// @param romHeader The header of the DSi rom to setup AES for.
|
||||
void SetupAes(const nds_header_twl_t* romHeader) const;
|
||||
|
||||
/// @brief Performs modcrypt decryption.
|
||||
/// @param data The data to decrypt.
|
||||
/// @param length The length of the data to decrypt.
|
||||
/// @param iv The AES initialization vector.
|
||||
void DecryptModuleAes(void* data, u32 length, const aes_u128_t* iv) const;
|
||||
|
||||
private:
|
||||
void SetupModuleKeyXY(const nds_header_twl_t* romHeader) const;
|
||||
void SetupNandKeyX() const;
|
||||
};
|
||||
52
arm7/source/loader/dldiHeader.h
Normal file
52
arm7/source/loader/dldiHeader.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
#define DLDI_MAGIC 0xBF8DA5ED
|
||||
#define DLDI_DRIVER_MAGIC_NONE 0x49444C44
|
||||
|
||||
#define DLDI_FIX_ALL (1 << 0)
|
||||
#define DLDI_FIX_GLUE (1 << 1)
|
||||
#define DLDI_FIX_GOT (1 << 2)
|
||||
#define DLDI_FIX_BSS (1 << 3)
|
||||
|
||||
#define DLDI_FEATURE_CANREAD (1 << 0)
|
||||
#define DLDI_FEATURE_CANWRITE (1 << 1)
|
||||
#define DLDI_FEATURE_SLOT_GBA (1 << 4)
|
||||
#define DLDI_FEATURE_SLOT_NDS (1 << 5)
|
||||
|
||||
typedef bool (*dldi_startup_func_t)(void);
|
||||
typedef bool (*dldi_inserted_func_t)(void);
|
||||
typedef bool (*dldi_read_func_t)(u32 sector, u32 count, void* dst);
|
||||
typedef bool (*dldi_write_func_t)(u32 sector, u32 count, const void* src);
|
||||
typedef bool (*dldi_clearstatus_func_t)(void);
|
||||
typedef bool (*dldi_shutdown_func_t)(void);
|
||||
|
||||
/// @brief Struct representing a DLDI header.
|
||||
struct dldi_header_t
|
||||
{
|
||||
u32 dldiMagic;
|
||||
u8 dldiString[8];
|
||||
u8 dldiVersion;
|
||||
u8 driverSize;
|
||||
u8 fixFlags;
|
||||
u8 stubSize;
|
||||
u8 driverName[0x30];
|
||||
u32 driverStartAddress;
|
||||
u32 driverEndAddress;
|
||||
u32 glueStartAddress;
|
||||
u32 glueEndAddress;
|
||||
u32 gotStartAddress;
|
||||
u32 gotEndAddress;
|
||||
u32 bssStartAddress;
|
||||
u32 bssEndAddress;
|
||||
u32 driverMagic;
|
||||
u32 featureFlags;
|
||||
u32 startupFuncAddress;
|
||||
u32 isInsertedFuncAddress;
|
||||
u32 readSectorsFuncAddress;
|
||||
u32 writeSectorsFuncAddress;
|
||||
u32 clearStatusFuncAddress;
|
||||
u32 shutdownFuncAddress;
|
||||
};
|
||||
|
||||
static_assert(sizeof(dldi_header_t) == 0x80, "Size of dldi_header_t incorrect");
|
||||
Reference in New Issue
Block a user