melonDS-emu / melonDS

DS emulator, sorta
https://melonds.kuribo64.net
GNU General Public License v3.0
3.16k stars 519 forks source link

Savefiles to EEPROM in Homebrew Games #1848

Open C3RV1 opened 11 months ago

C3RV1 commented 11 months ago

From MelonDS 0.9.2 onward homebrew games cannot simulate saving savefiles to an EEPROM. The reason for this is found in NDSCart.cpp line 1675:

if (homebrew)
    romparams.SaveMemType = 0; // no saveRAM for homebrew
else
    romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME)

Also the CartHomebrew class utilizes the default CartCommon::SPIWrite implementation:

u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last)
{
    return 0xFF;
}

causing homebrew games to only read 0xFF when reading REG_AUXSPIDATA, even after a command. This isn't correct, as after issuing a SPI_EEPROM_RDSR (read status register) the cart shouldn't return 0xFF. CartRetail instead implements it as:

u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
{
    switch (SRAMCmd)
    {
         // ...
    case 0x05: // read status register
        return SRAMStatus | 0xF0;
         // ...
    }
}

Some homebrew games expect bit 0 of RDSR to not be set before continuing to write data. From gbatek, bit 0 represents:

Bit0    WIP Write/Program/Erase in Progess (0=No, 1=Busy)
Bit1    WEL Write Enable Latch             (0=No, 1=Enable)
Bit7-2  Not used (zero)

So waiting for bit 0 to not be set, if it always reads 0xFF, the game hangs in an infinite loop. For example, in a function like:

void cardWaitInProgress() {
    cardCommand(SPI_EEPROM_RDSR, false);
    eepromWaitBusy();
    while (REG_AUXSPIDATA & 1) {  // hangs here
        cardCommand(SPI_EEPROM_RDSR, false);
        eepromWaitBusy();
    }
}

All of this doesn't allow homebrew to use an EEPROM for savefiles. Thus, homebrew games cannot save data unless DLDI is enabled, and the save file is saved to the SD image.

Could it be possible to add the option for Homebrew games to save savefile data to an EEPROM maybe by assuming its size? Alternatively, it would be better to make Homebrew games not hang by returning 0x0 when a RDSR command is issued (and returning valid responses to other commands too), although that would still cause the games to be unable to save.

JesseTG commented 11 months ago

Which homebrew games use EEPROM?

C3RV1 commented 11 months ago

Mine for example: https://github.com/C3RV1/UndertaleNDS

That's an issue I've encountered on my project, but still, I think Homebrew games should be able to save savefile data without needing to set up DLDI and an SD image. I know of some other developers who right now are setting up their games to work in a similar fashion.

Saving savefiles this way is easier, both for the player (it generates a .sav file) and for developers, who don't have to add instructions for users to setup a DLDI and SD image on emulators.

C3RV1 commented 11 months ago

On hardware, twilight menu, flashcards... I make sure to use a fat file system for saving the file instead, because I know whatever loaded the game is compatible with DLDI and has an SD image.

asiekierka commented 11 months ago

I think Homebrew games should be able to save savefile data without needing to set up DLDI and an SD image.

That is not, in my opinion, a good idea. An emulator should mimic environments which actually exist in the real world; people should not be writing code for emulators specifically. As there is no real world homebrew execution environment which uses EEPROM, I think adding such a feature in an emulator-exclusive manner is unwise.

Rather, it might be a good idea to, say, automatically prompt the user to generate an SD image as soon as a homebrew program tries to perform a DLDI access.

WiIIiam278 commented 11 months ago

As there is no real world homebrew execution environment which uses EEPROM,

Well, Godmode9i (a homebrew app) runs on hardware (an execution environment) and utilizes dkp EEPROM calls for dumping save files from cartridges. Obviously this use case is pointless on melon, but I think C3RVI (and my) use cases (supporting EEPROM calls as a fallback in non-DLDI environments) isn't unreasonable particularly, and is supported & works great on DESMUME (which has lacklustre DLDI support from my experience).

I agree with your point of principle though, but think homebrew games that want to use EEPROM should be able to, particularly given how unreliable DLDI/libfat can be.

RSDuck commented 11 months ago

Godmode9i (a homebrew app) runs on hardware (an execution environment) and utilizes dkp EEPROM calls for dumping save files from cartridges

you can run it from unlaunch in melonDS DSi mode with a cartridge inserted and it should already work in melonDS.

JesseTG commented 9 months ago

I just realized...if this were implemented, it would considerably simplify the test suite. You wouldn't need to include a retail ROM in the test pipeline.