From fa741b265bec4b3262fdbfabfd313432e9b4a000 Mon Sep 17 00:00:00 2001 From: Balint Kovacs Date: Sat, 13 Jul 2019 02:14:46 +0200 Subject: [PATCH] Write support for NTR saves Why does this work? We are writing flash memories without erasing them, and writing pages without regard to alignment. Yet it works? --- arm9/source/filesys/fsperm.c | 8 ++++++-- arm9/source/filesys/fsperm.h | 4 ++-- arm9/source/gamecart/gamecart.c | 2 +- arm9/source/gamecart/gamecart.h | 2 +- arm9/source/gamecart/spi.c | 4 ++-- arm9/source/gamecart/spi.h | 2 +- arm9/source/virtual/vcart.c | 10 +++++++++- arm9/source/virtual/vcart.h | 2 +- arm9/source/virtual/virtual.c | 4 +++- 9 files changed, 26 insertions(+), 12 deletions(-) diff --git a/arm9/source/filesys/fsperm.c b/arm9/source/filesys/fsperm.c index f331ba7..16be16b 100644 --- a/arm9/source/filesys/fsperm.c +++ b/arm9/source/filesys/fsperm.c @@ -69,7 +69,7 @@ bool CheckWritePermissions(const char* path) { snprintf(area_name, 16, "game images"); } else if (drvtype & DRV_CART) { perm = PERM_CART; - snprintf(area_name, 16, "gamecarts"); + snprintf(area_name, 16, "gamecart saves"); } else if (drvtype & DRV_VRAM) { perm = PERM_VRAM; snprintf(area_name, 16, "vram0"); @@ -100,7 +100,7 @@ bool CheckWritePermissions(const char* path) { return true; // offer unlock if possible - if (!(perm & (PERM_VRAM|PERM_CART|PERM_GAME|PERM_XORPAD))) { + if (!(perm & (PERM_VRAM|PERM_GAME|PERM_XORPAD))) { // ask the user if (!ShowPrompt(true, "Writing to %s is locked!\nUnlock it now?", area_name)) return false; @@ -169,6 +169,10 @@ bool SetWritePermissions(u32 perm, bool add_perm) { if (!ShowUnlockSequence(2, "You want to enable SD data\nwriting permissions.\n \nThis enables you to modify\ninstallations, user data &\nsavegames.")) return false; break; + case PERM_CART: + if (!ShowUnlockSequence(2, "You want to enable gamecart\nsave writing permissions.")) + return false; + break; #ifndef SAFEMODE case PERM_SYS_LVL2: if (!ShowUnlockSequence(3, "!Better be careful!\n \nYou want to enable SysNAND\nlvl2 writing permissions.\n \nThis enables you to modify\nirrecoverable system data!")) diff --git a/arm9/source/filesys/fsperm.h b/arm9/source/filesys/fsperm.h index b5cabb9..a77f397 100644 --- a/arm9/source/filesys/fsperm.h +++ b/arm9/source/filesys/fsperm.h @@ -16,7 +16,7 @@ #define PERM_MEMORY (1UL<<10) #define PERM_GAME (1UL<<11) // can't be enabled, placeholder #define PERM_XORPAD (1UL<<12) // can't be enabled, placeholder -#define PERM_CART (1UL<<13) // can't be enabled, placeholder +#define PERM_CART (1UL<<13) #define PERM_VRAM (1UL<<14) // can't be enabled, placeholder #define PERM_BASE (PERM_SDCARD | PERM_IMAGE | PERM_RAMDRIVE | PERM_EMU_LVL0 | PERM_SYS_LVL0) @@ -24,7 +24,7 @@ #define PERM_BLUE (GetWritePermissions()&PERM_MEMORY) #define PERM_RED (GetWritePermissions()&(PERM_SYS_LVL3&~PERM_SYS_LVL2)) #define PERM_ORANGE (GetWritePermissions()&(PERM_SYS_LVL2&~PERM_SYS_LVL1)) -#define PERM_YELLOW (GetWritePermissions()&((PERM_SYS_LVL1&~PERM_SYS_LVL0)|(PERM_EMU_LVL1&~PERM_EMU_LVL0)|(PERM_SDDATA&~PERM_SDCARD))) +#define PERM_YELLOW (GetWritePermissions()&((PERM_SYS_LVL1&~PERM_SYS_LVL0)|(PERM_EMU_LVL1&~PERM_EMU_LVL0)|(PERM_SDDATA&~PERM_SDCARD)|PERM_CART)) #define PERM_GREEN (GetWritePermissions()&(PERM_SDCARD|PERM_IMAGE|PERM_RAMDRIVE|PERM_EMU_LVL0|PERM_SYS_LVL0)) /** Check if writing to this path is allowed **/ diff --git a/arm9/source/gamecart/gamecart.c b/arm9/source/gamecart/gamecart.c index 7812c07..293a650 100644 --- a/arm9/source/gamecart/gamecart.c +++ b/arm9/source/gamecart/gamecart.c @@ -247,7 +247,7 @@ u32 ReadCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata) { return (SPIReadSaveData((CardType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; } -u32 WriteCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata) { +u32 WriteCartSave(const u8* buffer, u64 offset, u64 count, CartData* cdata) { if (offset >= cdata->save_size) return 1; if (offset + count > cdata->save_size) count = cdata->save_size - offset; return (SPIWriteSaveData((CardType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; diff --git a/arm9/source/gamecart/gamecart.h b/arm9/source/gamecart/gamecart.h index 77a70c3..9942e0e 100644 --- a/arm9/source/gamecart/gamecart.h +++ b/arm9/source/gamecart/gamecart.h @@ -30,5 +30,5 @@ u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata); u32 ReadCartBytes(void* buffer, u64 offset, u64 count, CartData* cdata); u32 ReadCartPrivateHeader(void* buffer, u64 offset, u64 count, CartData* cdata); u32 ReadCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata); -u32 WriteCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata); +u32 WriteCartSave(const u8* buffer, u64 offset, u64 count, CartData* cdata); u32 ReadCartSaveJedecId(u8* buffer, u64 offset, u64 count, CartData* cdata); diff --git a/arm9/source/gamecart/spi.c b/arm9/source/gamecart/spi.c index 99954a3..6f9aaff 100644 --- a/arm9/source/gamecart/spi.c +++ b/arm9/source/gamecart/spi.c @@ -106,7 +106,7 @@ u32 SPIGetCapacity(CardType type) { else return 1 << sz[(int) type]; } -int SPIWriteSaveData(CardType type, u32 offset, void* data, u32 size) { +int SPIWriteSaveData(CardType type, u32 offset, const void* data, u32 size) { u8 cmd[4] = { 0 }; u32 cmdSize = 4; @@ -167,7 +167,7 @@ int SPIWriteSaveData(CardType type, u32 offset, void* data, u32 size) { case FLASH_8MB: return 0xC8E13404; // writing is unsupported (so is reading? need to test) default: - return 0; // never happens + return -1; // never happens } u32 remaining = end - pos; diff --git a/arm9/source/gamecart/spi.h b/arm9/source/gamecart/spi.h index be9f5c6..8be9187 100644 --- a/arm9/source/gamecart/spi.h +++ b/arm9/source/gamecart/spi.h @@ -89,7 +89,7 @@ int SPIGetCardType(CardType* type, int infrared); u32 SPIGetPageSize(CardType type); u32 SPIGetCapacity(CardType type); -int SPIWriteSaveData(CardType type, u32 offset, void* data, u32 size); +int SPIWriteSaveData(CardType type, u32 offset, const void* data, u32 size); int SPIReadSaveData(CardType type, u32 offset, void* data, u32 size); // int SPIEraseSector(CardType type, u32 offset); diff --git a/arm9/source/virtual/vcart.c b/arm9/source/virtual/vcart.c index 098074d..eefa390 100644 --- a/arm9/source/virtual/vcart.c +++ b/arm9/source/virtual/vcart.c @@ -65,7 +65,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) { } else if (vdir->index == 7) { // JEDEC id and status register strcpy(vfile->name, "jedecid_and_sreg.bin"); vfile->size = JEDECID_AND_SREG_SIZE; - vfile->flags = VFLAG_JEDECID_AND_SRFG; + vfile->flags |= VFLAG_JEDECID_AND_SRFG; return true; } } @@ -85,6 +85,14 @@ int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) else return ReadCartBytes(buffer, foffset, count, cdata); } +int WriteVCartFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) { + if (!cdata) return -1; + if (vfile->flags & VFLAG_SAVEGAME) { + return WriteCartSave(buffer, offset, count, cdata); + } + return -1; +} + u64 GetVCartDriveSize(void) { return cart_init ? cdata->cart_size : 0; } diff --git a/arm9/source/virtual/vcart.h b/arm9/source/virtual/vcart.h index 9b3825d..976a77b 100644 --- a/arm9/source/virtual/vcart.h +++ b/arm9/source/virtual/vcart.h @@ -6,6 +6,6 @@ u32 InitVCartDrive(void); bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir); int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count); -// int WriteVCartFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count); // no writes +int WriteVCartFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count); u64 GetVCartDriveSize(void); void GetVCartTypeString(char* typestr); diff --git a/arm9/source/virtual/virtual.c b/arm9/source/virtual/virtual.c index c80e2d7..98fd83b 100644 --- a/arm9/source/virtual/virtual.c +++ b/arm9/source/virtual/virtual.c @@ -204,7 +204,9 @@ int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u return WriteVMemFile(vfile, buffer, offset, count); } else if (vfile->flags & VRT_DISADIFF) { return WriteVDisaDiffFile(vfile, buffer, offset, count); - } // no write support for virtual game / tickdb / keydb / cart / vram files + } else if (vfile->flags & VRT_CART) { + return WriteVCartFile(vfile, buffer, offset, count); + } // no write support for virtual game / tickdb / keydb / vram files return -1; }