366 lines
9.0 KiB
C++
366 lines
9.0 KiB
C++
/*
|
|
Copyright 2023 flyinghead
|
|
|
|
This file is part of Flycast.
|
|
|
|
Flycast is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Flycast is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "nvmem.h"
|
|
#include "flashrom.h"
|
|
#include "cfg/option.h"
|
|
#include "hw/aica/aica_if.h"
|
|
#include "reios/reios.h"
|
|
#include "oslib/oslib.h"
|
|
|
|
extern bool bios_loaded;
|
|
|
|
namespace nvmem
|
|
{
|
|
|
|
static MemChip *sys_rom;
|
|
static WritableChip *sys_nvmem;
|
|
|
|
static std::string getRomPrefix()
|
|
{
|
|
switch (settings.platform.system)
|
|
{
|
|
case DC_PLATFORM_DREAMCAST:
|
|
return "dc_";
|
|
case DC_PLATFORM_NAOMI:
|
|
return "naomi_";
|
|
case DC_PLATFORM_NAOMI2:
|
|
return "naomi2_";
|
|
case DC_PLATFORM_ATOMISWAVE:
|
|
return "aw_";
|
|
default:
|
|
die("Unsupported platform");
|
|
return "";
|
|
}
|
|
}
|
|
|
|
static void add_isp_to_nvmem(DCFlashChip *flash)
|
|
{
|
|
u8 block[64];
|
|
if (!flash->ReadBlock(FLASH_PT_USER, FLASH_USER_INET, block))
|
|
{
|
|
memset(block, 0, sizeof(block));
|
|
strcpy((char *)block + 2, "PWBrowser");
|
|
block[12] = 0x1c;
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_INET, block);
|
|
|
|
memset(block, 0, sizeof(block));
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_INET + 1, block);
|
|
strcpy((char *)block + 32, "AT&F");
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_INET + 2, block);
|
|
memset(block, 0, sizeof(block));
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_INET + 3, block);
|
|
memset(block + 27, 0xFF, sizeof(block) - 27);
|
|
block[10] = 1;
|
|
block[14] = 1;
|
|
block[16] = 1;
|
|
block[19] = 6;
|
|
block[26] = 5;
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_INET + 4, block);
|
|
memset(block, 0xFF, sizeof(block));
|
|
for (u32 i = FLASH_USER_INET + 5; i <= 0xbf; i++)
|
|
flash->WriteBlock(FLASH_PT_USER, i, block);
|
|
|
|
flash_isp1_block isp1;
|
|
memset(&isp1, 0, sizeof(isp1));
|
|
isp1._unknown[3] = 1;
|
|
memcpy(isp1.sega, "SEGA", 4);
|
|
strcpy(isp1.username, "flycast1");
|
|
strcpy(isp1.password, "password");
|
|
strcpy(isp1.phone, "1234567");
|
|
if (flash->WriteBlock(FLASH_PT_USER, FLASH_USER_ISP1, &isp1) != 1)
|
|
WARN_LOG(FLASHROM, "Failed to save ISP information to flash RAM");
|
|
|
|
memset(block, 0, sizeof(block));
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_ISP1 + 1, block);
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_ISP1 + 2, block);
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_ISP1 + 3, block);
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_ISP1 + 4, block);
|
|
block[60] = 1;
|
|
flash->WriteBlock(FLASH_PT_USER, FLASH_USER_ISP1 + 5, block);
|
|
|
|
flash_isp2_block isp2;
|
|
memset(&isp2, 0, sizeof(isp2));
|
|
memcpy(isp2.sega, "SEGA", 4);
|
|
strcpy(isp2.username, "flycast2");
|
|
strcpy(isp2.password, "password");
|
|
strcpy(isp2.phone, "1234567");
|
|
if (flash->WriteBlock(FLASH_PT_USER, FLASH_USER_ISP2, &isp2) != 1)
|
|
WARN_LOG(FLASHROM, "Failed to save ISP information to flash RAM");
|
|
u8 block[64];
|
|
memset(block, 0, sizeof(block));
|
|
for (u32 i = FLASH_USER_ISP2 + 1; i <= 0xEA; i++)
|
|
{
|
|
if (i == 0xcb)
|
|
block[56] = 1;
|
|
else
|
|
block[56] = 0;
|
|
flash->WriteBlock(FLASH_PT_USER, i, block);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fixUpDCFlash()
|
|
{
|
|
if (settings.platform.isConsole())
|
|
{
|
|
static_cast<DCFlashChip*>(sys_nvmem)->Validate();
|
|
|
|
// overwrite factory flash settings
|
|
if (config::Region <= 2)
|
|
{
|
|
sys_nvmem->data[0x1a002] = '0' + config::Region;
|
|
sys_nvmem->data[0x1a0a2] = '0' + config::Region;
|
|
}
|
|
if (config::Language <= 5)
|
|
{
|
|
sys_nvmem->data[0x1a003] = '0' + config::Language;
|
|
sys_nvmem->data[0x1a0a3] = '0' + config::Language;
|
|
}
|
|
if (config::Broadcast <= 3)
|
|
{
|
|
sys_nvmem->data[0x1a004] = '0' + config::Broadcast;
|
|
sys_nvmem->data[0x1a0a4] = '0' + config::Broadcast;
|
|
}
|
|
|
|
// overwrite user settings
|
|
struct flash_syscfg_block syscfg;
|
|
int res = static_cast<DCFlashChip*>(sys_nvmem)->ReadBlock(FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg);
|
|
|
|
if (!res)
|
|
{
|
|
// write out default settings
|
|
memset(&syscfg, 0xff, sizeof(syscfg));
|
|
syscfg.time_lo = 0;
|
|
syscfg.time_hi = 0;
|
|
syscfg.time_zone = 0;
|
|
syscfg.lang = 0;
|
|
syscfg.mono = 0;
|
|
syscfg.autostart = 1;
|
|
}
|
|
u32 now = aica::GetRTC_now();
|
|
syscfg.time_lo = now & 0xffff;
|
|
syscfg.time_hi = now >> 16;
|
|
if (config::Language <= 5)
|
|
syscfg.lang = config::Language;
|
|
|
|
if (static_cast<DCFlashChip*>(sys_nvmem)->WriteBlock(FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg) != 1)
|
|
WARN_LOG(FLASHROM, "Failed to save time and language to flash RAM");
|
|
|
|
add_isp_to_nvmem(static_cast<DCFlashChip*>(sys_nvmem));
|
|
|
|
// Check the console ID used by some network games (chuchu rocket)
|
|
u8 *console_id = &sys_nvmem->data[0x1A058];
|
|
if (!memcmp(console_id, "\377\377\377\377\377\377", 6))
|
|
{
|
|
srand(now);
|
|
u8 sum = 0;
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
console_id[i] = rand();
|
|
console_id[i + 0xA0] = console_id[i]; // copy at 1A0F8
|
|
sum += console_id[i];
|
|
}
|
|
console_id[-1] = console_id[0xA0 - 1] = sum;
|
|
console_id[-2] = console_id[0xA0 - 2] = ~sum;
|
|
}
|
|
else
|
|
{
|
|
// Fix checksum
|
|
u8 sum = 0;
|
|
for (int i = 0; i < 6; i++)
|
|
sum += console_id[i];
|
|
console_id[-1] = console_id[0xA0 - 1] = sum;
|
|
console_id[-2] = console_id[0xA0 - 2] = ~sum;
|
|
}
|
|
// must be != 0xff
|
|
console_id[7] = console_id[0xA0 + 7] = 0xfe;
|
|
}
|
|
}
|
|
|
|
static bool loadFlash()
|
|
{
|
|
bool rc = true;
|
|
if (settings.platform.isConsole())
|
|
rc = sys_nvmem->Load(getRomPrefix(), "%nvmem.bin", "nvram");
|
|
else if (!settings.naomi.slave)
|
|
rc = sys_nvmem->Load(hostfs::getArcadeFlashPath() + ".nvmem");
|
|
if (!rc)
|
|
INFO_LOG(FLASHROM, "flash/nvmem is missing, will create new file...");
|
|
fixUpDCFlash();
|
|
if (config::GGPOEnable)
|
|
sys_nvmem->digest(settings.network.md5.nvmem);
|
|
|
|
if (settings.platform.isAtomiswave())
|
|
{
|
|
sys_rom->Load(hostfs::getArcadeFlashPath() + ".nvmem2");
|
|
if (config::GGPOEnable)
|
|
sys_nvmem->digest(settings.network.md5.nvmem2);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool loadFiles()
|
|
{
|
|
loadFlash();
|
|
if (!settings.platform.isAtomiswave())
|
|
{
|
|
if (sys_rom->Load(getRomPrefix(), "%boot.bin;%boot.bin.bin;%bios.bin;%bios.bin.bin", "bootrom"))
|
|
{
|
|
if (config::GGPOEnable)
|
|
sys_rom->digest(settings.network.md5.bios);
|
|
bios_loaded = true;
|
|
}
|
|
else if (settings.platform.isConsole())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void saveFiles()
|
|
{
|
|
if (settings.naomi.slave || settings.naomi.drivingSimSlave)
|
|
return;
|
|
if (settings.platform.isConsole())
|
|
sys_nvmem->Save(getRomPrefix(), "nvmem.bin", "nvmem");
|
|
else
|
|
sys_nvmem->Save(hostfs::getArcadeFlashPath() + ".nvmem");
|
|
if (settings.platform.isAtomiswave())
|
|
((WritableChip *)sys_rom)->Save(hostfs::getArcadeFlashPath() + ".nvmem2");
|
|
}
|
|
|
|
bool loadHle()
|
|
{
|
|
if (!loadFlash())
|
|
WARN_LOG(FLASHROM, "No nvmem loaded");
|
|
|
|
reios_reset(sys_rom->data);
|
|
|
|
return true;
|
|
}
|
|
|
|
u32 readFlash(u32 addr, u32 sz)
|
|
{
|
|
return sys_nvmem->Read(addr, sz);
|
|
}
|
|
void writeFlash(u32 addr, u32 data, u32 sz)
|
|
{
|
|
sys_nvmem->Write(addr, data, sz);
|
|
}
|
|
u8 *getFlashData()
|
|
{
|
|
return sys_nvmem->data;
|
|
}
|
|
|
|
u32 readBios(u32 addr, u32 sz)
|
|
{
|
|
return sys_rom->Read(addr, sz);
|
|
}
|
|
void writeAWBios(u32 addr, u32 data, u32 sz)
|
|
{
|
|
((WritableChip *)sys_rom)->Write(addr, data, sz);
|
|
}
|
|
u8 *getBiosData()
|
|
{
|
|
return sys_rom->data;
|
|
}
|
|
void reloadAWBios()
|
|
{
|
|
if (settings.platform.isAtomiswave())
|
|
sys_rom->Reload();
|
|
}
|
|
|
|
void init()
|
|
{
|
|
switch (settings.platform.system)
|
|
{
|
|
case DC_PLATFORM_DREAMCAST:
|
|
sys_rom = new RomChip(settings.platform.bios_size);
|
|
sys_nvmem = new DCFlashChip(settings.platform.flash_size);
|
|
reios_set_flash(sys_nvmem);
|
|
break;
|
|
case DC_PLATFORM_NAOMI:
|
|
case DC_PLATFORM_NAOMI2:
|
|
sys_rom = new RomChip(settings.platform.bios_size);
|
|
sys_nvmem = new SRamChip(settings.platform.flash_size);
|
|
break;
|
|
case DC_PLATFORM_ATOMISWAVE:
|
|
sys_rom = new DCFlashChip(settings.platform.bios_size, settings.platform.bios_size / 2);
|
|
sys_nvmem = new SRamChip(settings.platform.flash_size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void term()
|
|
{
|
|
delete sys_rom;
|
|
sys_rom = nullptr;
|
|
delete sys_nvmem;
|
|
sys_nvmem = nullptr;
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
sys_rom->Reset();
|
|
sys_nvmem->Reset();
|
|
}
|
|
|
|
void serialize(Serializer& ser)
|
|
{
|
|
sys_rom->Serialize(ser);
|
|
sys_nvmem->Serialize(ser);
|
|
}
|
|
|
|
void deserialize(Deserializer& deser)
|
|
{
|
|
if (deser.version() <= Deserializer::VLAST_LIBRETRO)
|
|
{
|
|
deser.skip<u32>(); // size
|
|
deser.skip<u32>(); // mask
|
|
|
|
// Legacy libretro savestate
|
|
if (settings.platform.isArcade())
|
|
sys_nvmem->Deserialize(deser);
|
|
|
|
deser.skip<u32>(); // sys_nvmem/sys_rom->size
|
|
deser.skip<u32>(); // sys_nvmem/sys_rom->mask
|
|
if (settings.platform.isConsole())
|
|
{
|
|
sys_nvmem->Deserialize(deser);
|
|
}
|
|
else if (settings.platform.isAtomiswave())
|
|
{
|
|
deser >> static_cast<DCFlashChip*>(sys_rom)->state;
|
|
deser.deserialize(sys_rom->data, sys_rom->size);
|
|
}
|
|
else
|
|
{
|
|
deser.skip<u32>();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sys_rom->Deserialize(deser);
|
|
sys_nvmem->Deserialize(deser);
|
|
}
|
|
}
|
|
|
|
}
|