diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 6432703c..3524739b 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -119,6 +119,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 5e6db8e0..7cf8063e 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -2970,6 +2970,9 @@
Shared
+
+ SNES\Coprocessors\SufamiTurbo
+
@@ -3457,5 +3460,8 @@
{387c0b44-8063-45e8-a28a-b6d9e27b5a3f}
+
+ {bfdd4bb9-41c2-4255-a8c3-0176c990cbf3}
+
\ No newline at end of file
diff --git a/Core/Debugger/DebugUtilities.h b/Core/Debugger/DebugUtilities.h
index 74abb206..dfb87ac3 100644
--- a/Core/Debugger/DebugUtilities.h
+++ b/Core/Debugger/DebugUtilities.h
@@ -61,6 +61,9 @@ public:
case MemoryType::SnesWorkRam:
case MemoryType::BsxMemoryPack:
case MemoryType::BsxPsRam:
+ case MemoryType::SufamiTurboFirmware:
+ case MemoryType::SufamiTurboSecondCart:
+ case MemoryType::SufamiTurboSecondCartRam:
case MemoryType::SnesRegister:
return CpuType::Snes;
@@ -232,6 +235,8 @@ public:
case MemoryType::DspProgramRom:
case MemoryType::St018PrgRom:
case MemoryType::St018DataRom:
+ case MemoryType::SufamiTurboFirmware:
+ case MemoryType::SufamiTurboSecondCart:
case MemoryType::SpcRom:
case MemoryType::SmsPrgRom:
case MemoryType::SmsBootRom:
@@ -255,6 +260,7 @@ public:
case MemoryType::NesSaveRam:
case MemoryType::GbCartRam:
case MemoryType::SnesSaveRam:
+ case MemoryType::SufamiTurboSecondCartRam:
case MemoryType::PceSaveRam:
case MemoryType::SnesRegister:
case MemoryType::SmsCartRam:
diff --git a/Core/SNES/BaseCartridge.cpp b/Core/SNES/BaseCartridge.cpp
index 73a48929..22f0c65b 100644
--- a/Core/SNES/BaseCartridge.cpp
+++ b/Core/SNES/BaseCartridge.cpp
@@ -19,6 +19,7 @@
#include "SNES/Coprocessors/BSX/BsxCart.h"
#include "SNES/Coprocessors/BSX/BsxMemoryPack.h"
#include "SNES/Coprocessors/SGB/SuperGameboy.h"
+#include "SNES/Coprocessors/SufamiTurbo/SufamiTurbo.h"
#include "Shared/EmuSettings.h"
#include "Shared/SettingTypes.h"
#include "Shared/BatteryManager.h"
@@ -64,12 +65,18 @@ unique_ptr BaseCartridge::CreateCartridge(SnesConsole* console, V
}
cart->LoadRom();
cart->_emu->RegisterMemory(MemoryType::SnesPrgRom, cart->_prgRom, cart->_prgRomSize);
+ } else if(fileExt == ".st") {
+ if(cart->LoadSufamiTurbo(romFile)) {
+ return cart;
+ } else {
+ return nullptr;
+ }
} else if(fileExt == ".gb" || fileExt == ".gbc" || fileExt == ".gbx") {
if(cart->LoadGameboy(romFile)) {
return cart;
} else {
return nullptr;
- }
+ }
} else {
if(romData.size() < 0x8000) {
return nullptr;
@@ -88,7 +95,7 @@ unique_ptr BaseCartridge::CreateCartridge(SnesConsole* console, V
}
} else {
cart->LoadRom();
- cart->EnsureValidPrgRomSize();
+ BaseCartridge::EnsureValidPrgRomSize(cart->_prgRomSize, cart->_prgRom);
}
cart->_emu->RegisterMemory(MemoryType::SnesPrgRom, cart->_prgRom, cart->_prgRomSize);
@@ -100,18 +107,18 @@ unique_ptr BaseCartridge::CreateCartridge(SnesConsole* console, V
}
}
-void BaseCartridge::EnsureValidPrgRomSize()
+void BaseCartridge::EnsureValidPrgRomSize(uint32_t& size, uint8_t*& rom)
{
- if((_prgRomSize & 0xFFF) != 0) {
+ if((size & 0xFFF) != 0) {
//Round up to the next 4kb size, to ensure we have access to all the rom's data
//Memory mappings expect a multiple of 4kb to work properly
- uint32_t orgPrgSize = _prgRomSize;
- _prgRomSize = (_prgRomSize & ~0xFFF) + 0x1000;
- uint8_t* expandedPrgRom = new uint8_t[_prgRomSize];
- memset(expandedPrgRom, 0, _prgRomSize);
- memcpy(expandedPrgRom, _prgRom, orgPrgSize);
- delete[] _prgRom;
- _prgRom = expandedPrgRom;
+ uint32_t orgPrgSize = size;
+ size = (size & ~0xFFF) + 0x1000;
+ uint8_t* expandedPrgRom = new uint8_t[size];
+ memset(expandedPrgRom, 0, size);
+ memcpy(expandedPrgRom, rom, orgPrgSize);
+ delete[] rom;
+ rom = expandedPrgRom;
}
}
@@ -404,6 +411,10 @@ void BaseCartridge::SaveBattery()
if(_gameboy) {
_gameboy->SaveBattery();
}
+
+ if(_sufamiTurbo) {
+ _sufamiTurbo->SaveBattery();
+ }
}
void BaseCartridge::Init(MemoryMappings &mm)
@@ -555,7 +566,11 @@ bool BaseCartridge::MapSpecificCarts(MemoryMappings &mm)
{
string name = GetCartName();
string code = GetGameCode();
- if(GetCartName() == "DEZAEMON") {
+
+ if(_sufamiTurbo) {
+ _sufamiTurbo->InitializeMappings(mm, _prgRomHandlers, _saveRamHandlers);
+ return true;
+ } else if(GetCartName() == "DEZAEMON") {
//LOROM with mirrored SRAM?
mm.RegisterHandler(0x00, 0x7D, 0x8000, 0xFFFF, _prgRomHandlers);
mm.RegisterHandler(0x80, 0xFF, 0x8000, 0xFFFF, _prgRomHandlers);
@@ -644,6 +659,30 @@ void BaseCartridge::LoadSpc()
SetupCpuHalt();
}
+bool BaseCartridge::LoadSufamiTurbo(VirtualFile& romFile)
+{
+ _sufamiTurbo.reset(SufamiTurbo::Init(_emu, romFile));
+ if(!_sufamiTurbo) {
+ return false;
+ }
+
+ vector romData;
+ romFile.ReadFile(romData);
+
+ _prgRomSize = (uint32_t)romData.size();
+ _prgRom = new uint8_t[_prgRomSize];
+ memcpy(_prgRom, romData.data(), romData.size());
+ BaseCartridge::EnsureValidPrgRomSize(_prgRomSize, _prgRom);
+ _emu->RegisterMemory(MemoryType::SnesPrgRom, _prgRom, _prgRomSize);
+
+ _saveRamSize = SufamiTurbo::GetSaveRamSize(romData);
+ _saveRam = new uint8_t[_saveRamSize];
+ _emu->RegisterMemory(MemoryType::SnesSaveRam, _saveRam, _saveRamSize);
+ _emu->GetSettings()->InitializeRam(GetRamPowerOnState(), _saveRam, _saveRamSize);
+
+ return true;
+}
+
bool BaseCartridge::LoadGameboy(VirtualFile& romFile)
{
_cartInfo = { };
diff --git a/Core/SNES/BaseCartridge.h b/Core/SNES/BaseCartridge.h
index dd61dd84..80c3abad 100644
--- a/Core/SNES/BaseCartridge.h
+++ b/Core/SNES/BaseCartridge.h
@@ -21,6 +21,7 @@ class Gameboy;
class SnesConsole;
class Emulator;
class SpcFileData;
+class SufamiTurbo;
enum class ConsoleRegion;
enum class RamState;
@@ -47,6 +48,7 @@ private:
BsxCart* _bsx = nullptr;
unique_ptr _bsxMemPack;
unique_ptr _gameboy;
+ unique_ptr _sufamiTurbo;
CartFlags::CartFlags _flags = CartFlags::CartFlags::None;
CoprocessorType _coprocessorType = CoprocessorType::None;
@@ -83,10 +85,13 @@ private:
void InitRamPowerOnState();
void LoadRom();
- void EnsureValidPrgRomSize();
void LoadSpc();
+
+ bool LoadSufamiTurbo(VirtualFile& romFile);
+
bool LoadGameboy(VirtualFile& romFile);
+
void SetupCpuHalt();
void InitCoprocessor();
void LoadEmbeddedFirmware();
@@ -99,6 +104,8 @@ public:
static unique_ptr CreateCartridge(SnesConsole* console, VirtualFile &romFile);
+ static void EnsureValidPrgRomSize(uint32_t& size, uint8_t*& rom);
+
void Reset();
void SaveBattery();
diff --git a/Core/SNES/Coprocessors/SufamiTurbo/SufamiTurbo.h b/Core/SNES/Coprocessors/SufamiTurbo/SufamiTurbo.h
new file mode 100644
index 00000000..b485d29d
--- /dev/null
+++ b/Core/SNES/Coprocessors/SufamiTurbo/SufamiTurbo.h
@@ -0,0 +1,148 @@
+#pragma once
+#include "pch.h"
+#include "SNES/IMemoryHandler.h"
+#include "SNES/RomHandler.h"
+#include "SNES/MemoryMappings.h"
+#include "Shared/Emulator.h"
+#include "Shared/BatteryManager.h"
+#include "Shared/FirmwareHelper.h"
+#include "Utilities/VirtualFile.h"
+
+struct SufamiTurboFilePromptMessage
+{
+ char Filename[5000];
+};
+
+class SufamiTurbo
+{
+private:
+ Emulator* _emu = nullptr;
+ string _nameSlotA;
+
+ uint8_t* _firmware = nullptr;
+ uint32_t _firmwareSize = 0;
+
+ string _cartName;
+ uint8_t* _cartRom = nullptr;
+ uint32_t _cartRomSize = 0;
+
+ uint8_t* _cartRam = nullptr;
+ uint32_t _cartRamSize = 0;
+
+ vector> _firmwareHandlers;
+ vector> _cartRomHandlers;
+ vector> _cartRamHandlers;
+
+ SufamiTurbo() {}
+
+public:
+ static SufamiTurbo* Init(Emulator* emu, VirtualFile& slotA)
+ {
+ vector firmware;
+ if(!FirmwareHelper::LoadSufamiTurboFirmware(emu, firmware)) {
+ return nullptr;
+ }
+
+ SufamiTurbo* st = new SufamiTurbo();
+ st->_emu = emu;
+ st->_nameSlotA = FolderUtilities::GetFilename(slotA.GetFileName(), false);
+
+ st->_firmwareSize = (uint32_t)firmware.size();
+ st->_firmware = new uint8_t[st->_firmwareSize];
+ memcpy(st->_firmware, firmware.data(), firmware.size());
+ BaseCartridge::EnsureValidPrgRomSize(st->_firmwareSize, st->_firmware);
+ emu->RegisterMemory(MemoryType::SufamiTurboFirmware, st->_firmware, st->_firmwareSize);
+ for(uint32_t i = 0; i < st->_firmwareSize; i += 0x1000) {
+ st->_firmwareHandlers.push_back(unique_ptr(new RomHandler(st->_firmware, i, st->_firmwareSize, MemoryType::SufamiTurboFirmware)));
+ }
+
+ SufamiTurboFilePromptMessage msg = {};
+ emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::SufamiTurboFilePrompt, &msg);
+
+ string slot2File = string(msg.Filename, strlen(msg.Filename));
+ if(slot2File.size()) {
+ VirtualFile file = slot2File;
+ if(file.IsValid()) {
+ vector cart;
+ file.ReadFile(cart);
+
+ st->_cartName = FolderUtilities::GetFilename(file.GetFileName(), false);
+ if(st->_nameSlotA == st->_cartName) {
+ st->_cartName += "_SlotB";
+ }
+
+ st->_cartRomSize = (uint32_t)cart.size();
+ st->_cartRom = new uint8_t[st->_cartRomSize];
+ memcpy(st->_cartRom, cart.data(), cart.size());
+ BaseCartridge::EnsureValidPrgRomSize(st->_cartRomSize, st->_cartRom);
+
+ emu->RegisterMemory(MemoryType::SufamiTurboSecondCart, st->_cartRom, st->_cartRomSize);
+
+ for(uint32_t i = 0; i < st->_cartRomSize; i += 0x1000) {
+ st->_cartRomHandlers.push_back(unique_ptr(new RomHandler(st->_cartRom, i, st->_cartRomSize, MemoryType::SufamiTurboSecondCart)));
+ }
+
+ st->_cartRamSize = GetSaveRamSize(cart);
+ st->_cartRam = new uint8_t[st->_cartRamSize];
+ emu->RegisterMemory(MemoryType::SufamiTurboSecondCartRam, st->_cartRam, st->_cartRamSize);
+ memset(st->_cartRam, 0, st->_cartRamSize);
+
+ emu->GetBatteryManager()->LoadBattery(st->_cartName + ".srm", st->_cartRam, st->_cartRamSize);
+
+ for(uint32_t i = 0; i < st->_cartRamSize; i += 0x1000) {
+ st->_cartRamHandlers.push_back(unique_ptr(new RamHandler(st->_cartRam, i, st->_cartRamSize, MemoryType::SufamiTurboSecondCartRam)));
+ }
+ }
+ }
+
+ return st;
+ }
+
+ static uint32_t GetSaveRamSize(vector& cart)
+ {
+ auto checkMarker = [&](string marker) {
+ return std::search((char*)cart.data(), (char*)cart.data()+cart.size(), marker.c_str(), marker.c_str()+marker.size()) != (char*)cart.data() + cart.size();
+ };
+
+ if(checkMarker("POIPOI.Ver") || checkMarker("SDBATTLE ")) {
+ return 0x800;
+ } else if(checkMarker("SD \xB6\xDE\xDD\xC0\xDE\xD1 GN")) {
+ //SD ガンダム GN
+ return 0x2000;
+ }
+
+ return 0;
+ }
+
+ void InitializeMappings(MemoryMappings& mm, vector>& prgRomHandlers, vector>& saveRamHandlers)
+ {
+ mm.RegisterHandler(0x20, 0x3F, 0x8000, 0xFFFF, prgRomHandlers);
+ mm.RegisterHandler(0xA0, 0xBF, 0x8000, 0xFFFF, prgRomHandlers);
+
+ mm.RegisterHandler(0x60, 0x63, 0x8000, 0xFFFF, saveRamHandlers);
+ mm.RegisterHandler(0xE0, 0xE3, 0x8000, 0xFFFF, saveRamHandlers);
+
+ mm.RegisterHandler(0x00, 0x1F, 0x8000, 0xFFFF, _firmwareHandlers);
+ mm.RegisterHandler(0x80, 0x9F, 0x8000, 0xFFFF, _firmwareHandlers);
+
+ mm.RegisterHandler(0x40, 0x5F, 0x8000, 0xFFFF, _cartRomHandlers);
+ mm.RegisterHandler(0xC0, 0xDF, 0x8000, 0xFFFF, _cartRomHandlers);
+
+ mm.RegisterHandler(0x70, 0x73, 0x8000, 0xFFFF, _cartRamHandlers);
+ mm.RegisterHandler(0xF0, 0xF3, 0x8000, 0xFFFF, _cartRamHandlers);
+ }
+
+ void SaveBattery()
+ {
+ if(_cartRam) {
+ _emu->GetBatteryManager()->SaveBattery(_cartName + ".srm", _cartRam, _cartRamSize);
+ }
+ }
+
+ ~SufamiTurbo()
+ {
+ delete[] _firmware;
+ delete[] _cartRom;
+ delete[] _cartRam;
+ }
+};
\ No newline at end of file
diff --git a/Core/SNES/SnesConsole.cpp b/Core/SNES/SnesConsole.cpp
index 1c683b93..e589070c 100644
--- a/Core/SNES/SnesConsole.cpp
+++ b/Core/SNES/SnesConsole.cpp
@@ -472,6 +472,7 @@ AddressInfo SnesConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpu
case MemoryType::SnesPrgRom:
case MemoryType::SnesWorkRam:
case MemoryType::SnesSaveRam:
+ case MemoryType::SufamiTurboFirmware:
{
if(!mappings) {
return unmapped;
diff --git a/Core/SNES/SnesConsole.h b/Core/SNES/SnesConsole.h
index f99df5b1..6a1efb56 100644
--- a/Core/SNES/SnesConsole.h
+++ b/Core/SNES/SnesConsole.h
@@ -72,7 +72,7 @@ public:
SnesConsole(Emulator* emu);
~SnesConsole();
- static vector GetSupportedExtensions() { return { ".sfc", ".swc", ".fig", ".smc", ".bs", ".gb", ".gbc", ".gbx", ".spc" }; }
+ static vector GetSupportedExtensions() { return { ".sfc", ".swc", ".fig", ".smc", ".bs", ".gb", ".gbc", ".gbx", ".spc", ".st" }; }
static vector GetSupportedSignatures() { return { "SNES-SPC700 Sound File Data" }; }
void Initialize();
diff --git a/Core/Shared/BatteryManager.cpp b/Core/Shared/BatteryManager.cpp
index fbb99102..958db2a0 100644
--- a/Core/Shared/BatteryManager.cpp
+++ b/Core/Shared/BatteryManager.cpp
@@ -2,6 +2,7 @@
#include "Shared/BatteryManager.h"
#include "Utilities/VirtualFile.h"
#include "Utilities/FolderUtilities.h"
+#include "Utilities/StringUtilities.h"
void BatteryManager::Initialize(string romName, bool setBatteryFlag)
{
@@ -9,9 +10,13 @@ void BatteryManager::Initialize(string romName, bool setBatteryFlag)
_hasBattery = setBatteryFlag;
}
-string BatteryManager::GetBasePath()
+string BatteryManager::GetBasePath(string& extension)
{
- return FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), _romName);
+ if(StringUtilities::StartsWith(extension, ".")) {
+ return FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), _romName + extension);
+ } else {
+ return FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), extension);
+ }
}
void BatteryManager::SetBatteryProvider(shared_ptr provider)
@@ -32,7 +37,7 @@ void BatteryManager::SaveBattery(string extension, uint8_t* data, uint32_t lengt
}
_hasBattery = true;
- ofstream out(GetBasePath() + extension, ios::binary);
+ ofstream out(GetBasePath(extension), ios::binary);
if(out) {
out.write((char*)data, length);
}
@@ -52,7 +57,7 @@ vector BatteryManager::LoadBattery(string extension)
//Used by movie player to provider initial state of ram at startup
batteryData = provider->LoadBattery(extension);
} else {
- VirtualFile file = GetBasePath() + extension;
+ VirtualFile file = GetBasePath(extension);
if(file.IsValid()) {
file.ReadFile(batteryData);
}
diff --git a/Core/Shared/BatteryManager.h b/Core/Shared/BatteryManager.h
index 329f0e68..54cd8683 100644
--- a/Core/Shared/BatteryManager.h
+++ b/Core/Shared/BatteryManager.h
@@ -22,7 +22,7 @@ private:
std::weak_ptr _provider;
std::weak_ptr _recorder;
- string GetBasePath();
+ string GetBasePath(string& extension);
public:
void Initialize(string romName, bool setBatteryFlag = false);
diff --git a/Core/Shared/FirmwareHelper.h b/Core/Shared/FirmwareHelper.h
index 5df7f24e..a00f0b0d 100644
--- a/Core/Shared/FirmwareHelper.h
+++ b/Core/Shared/FirmwareHelper.h
@@ -16,6 +16,7 @@ enum class FirmwareType
ST011,
ST018,
Satellaview,
+ SufamiTurbo,
Gameboy,
GameboyColor,
GameboyAdvance,
@@ -185,6 +186,25 @@ public:
return false;
}
+ static bool LoadSufamiTurboFirmware(Emulator* emu, vector& data)
+ {
+ string filename = "SufamiTurbo.sfc";
+
+ if(AttemptLoadFirmware(data, filename, 0x40000)) {
+ return true;
+ }
+
+ MissingFirmwareMessage msg(filename.c_str(), FirmwareType::SufamiTurbo, 0x40000);
+ emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
+
+ if(AttemptLoadFirmware(data, filename, 0x40000)) {
+ return true;
+ }
+
+ MessageManager::DisplayMessage("Error", "Could not find firmware file for Sufami Turbo");
+ return false;
+ }
+
static bool LoadSgbFirmware(Emulator* emu, uint8_t** prgRom, uint32_t& prgSize, bool useSgb2, bool promptForFirmware)
{
string filename = useSgb2 ? "SGB2.sfc" : "SGB1.sfc";
diff --git a/Core/Shared/Interfaces/INotificationListener.h b/Core/Shared/Interfaces/INotificationListener.h
index ee1391ac..076cef4a 100644
--- a/Core/Shared/Interfaces/INotificationListener.h
+++ b/Core/Shared/Interfaces/INotificationListener.h
@@ -22,6 +22,7 @@ enum class ConsoleNotificationType
ViewerRefresh,
EventViewerRefresh,
MissingFirmware,
+ SufamiTurboFilePrompt,
BeforeGameUnload,
BeforeGameLoad,
GameLoadFailed,
diff --git a/Core/Shared/MemoryType.h b/Core/Shared/MemoryType.h
index 56bdd1d5..609cbfec 100644
--- a/Core/Shared/MemoryType.h
+++ b/Core/Shared/MemoryType.h
@@ -38,6 +38,9 @@ enum class MemoryType
St018PrgRom,
St018DataRom,
St018WorkRam,
+ SufamiTurboFirmware,
+ SufamiTurboSecondCart,
+ SufamiTurboSecondCartRam,
GbPrgRom,
GbWorkRam,
diff --git a/UI/Config/IntegrationConfig.cs b/UI/Config/IntegrationConfig.cs
index 5ada2963..0bea090a 100644
--- a/UI/Config/IntegrationConfig.cs
+++ b/UI/Config/IntegrationConfig.cs
@@ -76,6 +76,8 @@ namespace Mesen.Config
case MemoryType.PcePrgRom:
case MemoryType.SmsPrgRom:
case MemoryType.SpcRom:
+ case MemoryType.SufamiTurboFirmware:
+ case MemoryType.SufamiTurboSecondCart:
case MemoryType.DspProgramRom:
case MemoryType.DspDataRom:
case MemoryType.GbBootRom:
@@ -107,11 +109,12 @@ namespace Mesen.Config
return ImportWorkRamLabels;
case MemoryType.SnesSaveRam:
+ case MemoryType.BsxMemoryPack:
+ case MemoryType.SufamiTurboSecondCartRam:
case MemoryType.NesSaveRam:
case MemoryType.PceSaveRam:
case MemoryType.GbCartRam:
case MemoryType.SmsCartRam:
- case MemoryType.BsxMemoryPack:
case MemoryType.GbaSaveRam:
case MemoryType.WsCartRam:
return ImportSaveRamLabels;
diff --git a/UI/Debugger/Utilities/SaveRomActionHelper.cs b/UI/Debugger/Utilities/SaveRomActionHelper.cs
index 0b67f803..e1e74c31 100644
--- a/UI/Debugger/Utilities/SaveRomActionHelper.cs
+++ b/UI/Debugger/Utilities/SaveRomActionHelper.cs
@@ -58,6 +58,7 @@ namespace Mesen.Debugger.Utilities
RomFormat.Sg => true,
RomFormat.ColecoVision => true,
RomFormat.Gba => true,
+ RomFormat.Ws => true,
_ => false
};
}
diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs
index 2fea8463..cd43fb1f 100644
--- a/UI/Interop/DebugApi.cs
+++ b/UI/Interop/DebugApi.cs
@@ -621,6 +621,9 @@ namespace Mesen.Interop
St018PrgRom,
St018DataRom,
St018WorkRam,
+ SufamiTurboFirmware,
+ SufamiTurboSecondCart,
+ SufamiTurboSecondCartRam,
GbPrgRom,
GbWorkRam,
diff --git a/UI/Interop/EmuApi.cs b/UI/Interop/EmuApi.cs
index 3c380177..e6ff861c 100644
--- a/UI/Interop/EmuApi.cs
+++ b/UI/Interop/EmuApi.cs
@@ -257,6 +257,7 @@ namespace Mesen.Interop
ST011,
ST018,
Satellaview,
+ SufamiTurbo,
Gameboy,
GameboyColor,
GameboyAdvance,
@@ -285,6 +286,12 @@ namespace Mesen.Interop
public UInt32 AltSize;
}
+ public struct SufamiTurboFilePromptMessage
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5000)]
+ public byte[] Filename;
+ }
+
public struct ExecuteShortcutParams
{
public EmulatorShortcut Shortcut;
diff --git a/UI/Interop/FirmwareTypeExtensions.cs b/UI/Interop/FirmwareTypeExtensions.cs
index 87a5b707..59d754f1 100644
--- a/UI/Interop/FirmwareTypeExtensions.cs
+++ b/UI/Interop/FirmwareTypeExtensions.cs
@@ -18,6 +18,8 @@ public static class FirmwareTypeExtensions
case FirmwareType.ST011: return new("st011.rom") { new(0xD000, "8B2B3F3F3E6E29F4D21D8BC736B400BC988B7D2214EBEE15643F01C1FEE2F364") };
case FirmwareType.ST018: return new("st018.rom") { new(0x28000, "6DF209AB5D2524D1839C038BE400AE5EB20DAFC14A3771A3239CD9E8ACD53806") };
+ case FirmwareType.SufamiTurbo: return new("SufamiTurbo.sfc") { new(0x40000, "EDACB453DA14F825F05D1134D6035F4BF034E55F7CFB97C70C4EE107EABC7342") };
+
case FirmwareType.Satellaview: return new("BS-X.bin") {
new(1024 * 1024,
"27CFDB99F7E4252BF3740D420147B63C4C88616883BC5E7FE43F2F30BF8C8CBB", //Japan, no DRM
diff --git a/UI/Interop/MemoryTypeExtensions.cs b/UI/Interop/MemoryTypeExtensions.cs
index 77a7aa29..ec1103f1 100644
--- a/UI/Interop/MemoryTypeExtensions.cs
+++ b/UI/Interop/MemoryTypeExtensions.cs
@@ -71,6 +71,9 @@ namespace Mesen.Interop
case MemoryType.SnesCgRam:
case MemoryType.BsxPsRam:
case MemoryType.BsxMemoryPack:
+ case MemoryType.SufamiTurboFirmware:
+ case MemoryType.SufamiTurboSecondCart:
+ case MemoryType.SufamiTurboSecondCartRam:
case MemoryType.SnesRegister:
return CpuType.Snes;
@@ -246,6 +249,8 @@ namespace Mesen.Interop
case MemoryType.DspProgramRom:
case MemoryType.St018PrgRom:
case MemoryType.St018DataRom:
+ case MemoryType.SufamiTurboFirmware:
+ case MemoryType.SufamiTurboSecondCart:
case MemoryType.SpcRom:
case MemoryType.SmsPrgRom:
case MemoryType.SmsBootRom:
@@ -272,6 +277,9 @@ namespace Mesen.Interop
case MemoryType.St018PrgRom:
case MemoryType.St018DataRom:
case MemoryType.St018WorkRam:
+ case MemoryType.SufamiTurboFirmware:
+ case MemoryType.SufamiTurboSecondCart:
+ case MemoryType.SufamiTurboSecondCartRam:
//Gameboy
case MemoryType.GbPrgRom:
@@ -461,6 +469,10 @@ namespace Mesen.Interop
MemoryType.BsxPsRam => "PSRAM",
MemoryType.BsxMemoryPack => "MPACK",
+
+ MemoryType.SufamiTurboFirmware => "BOOT",
+ MemoryType.SufamiTurboSecondCart => "BPRG",
+ MemoryType.SufamiTurboSecondCartRam => "BRAM",
MemoryType.GameboyMemory => "CPU",
MemoryType.GbPrgRom => "PRG",
diff --git a/UI/Interop/NotificationListener.cs b/UI/Interop/NotificationListener.cs
index 7ade30a2..afa38825 100644
--- a/UI/Interop/NotificationListener.cs
+++ b/UI/Interop/NotificationListener.cs
@@ -80,6 +80,7 @@ namespace Mesen.Interop
ViewerRefresh,
EventViewerRefresh,
MissingFirmware,
+ SufamiTurboFilePrompt,
BeforeGameUnload,
BeforeGameLoad,
GameLoadFailed,
diff --git a/UI/Localization/resources.en.xml b/UI/Localization/resources.en.xml
index 5633927d..3b286f3d 100644
--- a/UI/Localization/resources.en.xml
+++ b/UI/Localization/resources.en.xml
@@ -1725,6 +1725,8 @@
Save changes?
Keep changes?
+ Do you want to load a second Sufami Turbo ROM to insert into slot B?
+
This file does not match any of the known firmwares and may not work properly.
Expected (SHA256): {0}
Current (SHA256): {1}
This file does not have the required size.
Expected: {0} bytes
Current: {1} bytes
The following file will be deleted permanently. Are you sure?
{0}
@@ -2683,6 +2685,7 @@ E
ST010
ST011
ST018
+ Sufami Turbo
Satellaview (BS-X)
Game Boy CPU
Game Boy Color CPU
@@ -2749,7 +2752,10 @@ E
ST018 PRG ROM
ST018 Data ROM
ST018 Work RAM
-
+ Sufami Turbo Firmware
+ Sufami Slot B ROM
+ Sufami Slot B RAM
+
GB - CPU Memory
GB - PRG ROM
GB - Work RAM
diff --git a/UI/Utilities/FileDialogHelper.cs b/UI/Utilities/FileDialogHelper.cs
index 90cfab90..564d22ab 100644
--- a/UI/Utilities/FileDialogHelper.cs
+++ b/UI/Utilities/FileDialogHelper.cs
@@ -39,6 +39,7 @@ namespace Mesen.Utilities
public const string NesAsmLabelExt = "fns";
public const string BinExt = "bin";
public const string NesExt = "nes";
+ public const string SufamiTurboExt = "st";
public static async Task OpenFile(string? initialFolder, IRenderRoot? parent, params string[] extensions)
{
@@ -51,7 +52,7 @@ namespace Mesen.Utilities
foreach(string ext in extensions) {
if(ext == FileDialogHelper.RomExt) {
filter.Add(new FilePickerFileType("All ROM files") { Patterns = new List() {
- "*.sfc", "*.fig", "*.smc", "*.bs", "*.spc",
+ "*.sfc", "*.fig", "*.smc", "*.bs", "*.st", "*.spc",
"*.nes", "*.fds", "*.unif", "*.unf", "*.studybox", "*.nsf", "*.nsfe",
"*.gb", "*.gbc", "*.gbx", "*.gbs",
"*.pce", "*.sgx", "*.cue", "*.hes",
@@ -60,7 +61,7 @@ namespace Mesen.Utilities
"*.ws", "*.wsc",
"*.zip", "*.7z"
} });
- filter.Add(new FilePickerFileType("SNES ROM files") { Patterns = new List() { "*.sfc", "*.fig", "*.smc", "*.bs", "*.spc" } });
+ filter.Add(new FilePickerFileType("SNES ROM files") { Patterns = new List() { "*.sfc", "*.fig", "*.smc", "*.bs", "*.st", "*.spc" } });
filter.Add(new FilePickerFileType("NES ROM files") { Patterns = new List() { "*.nes", "*.fds", "*.unif", "*.unf", "*.studybox", "*.nsf", "*.nsfe" } });
filter.Add(new FilePickerFileType("GB ROM files") { Patterns = new List() { "*.gb", "*.gbc", "*.gbx", "*.gbs" } });
filter.Add(new FilePickerFileType("GBA ROM files") { Patterns = new List() { "*.gba" } });
diff --git a/UI/Utilities/FolderHelper.cs b/UI/Utilities/FolderHelper.cs
index d5b63652..bdebbe98 100644
--- a/UI/Utilities/FolderHelper.cs
+++ b/UI/Utilities/FolderHelper.cs
@@ -11,7 +11,7 @@ namespace Mesen.Utilities
public static class FolderHelper
{
private static HashSet _romExtensions = new HashSet() {
- ".sfc", ".smc", ".fig", ".swc", ".bs",
+ ".sfc", ".smc", ".fig", ".swc", ".bs", ".st",
".gb", ".gbc", ".gbx",
".nes", ".unif", ".unf", ".fds", ".studybox",
".pce", ".sgx", ".cue",
diff --git a/UI/Views/EmulationConfigView.axaml b/UI/Views/EmulationConfigView.axaml
index 8ebb0c21..b8a5dbdf 100644
--- a/UI/Views/EmulationConfigView.axaml
+++ b/UI/Views/EmulationConfigView.axaml
@@ -92,7 +92,7 @@
-
+
@@ -111,20 +111,23 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
diff --git a/UI/Windows/MainWindow.axaml.cs b/UI/Windows/MainWindow.axaml.cs
index 770858a7..9e22dab5 100644
--- a/UI/Windows/MainWindow.axaml.cs
+++ b/UI/Windows/MainWindow.axaml.cs
@@ -24,6 +24,7 @@ using Mesen.Controls;
using Mesen.Localization;
using System.Diagnostics;
using Avalonia.VisualTree;
+using System.Text;
namespace Mesen.Windows
{
@@ -367,7 +368,7 @@ namespace Mesen.Windows
});
break;
- case ConsoleNotificationType.MissingFirmware:
+ case ConsoleNotificationType.MissingFirmware: {
MissingFirmwareMessage msg = Marshal.PtrToStructure(e.Parameter);
TaskCompletionSource tcs = new TaskCompletionSource();
Dispatcher.UIThread.Post(async () => {
@@ -376,6 +377,25 @@ namespace Mesen.Windows
});
tcs.Task.Wait();
break;
+ }
+
+ case ConsoleNotificationType.SufamiTurboFilePrompt: {
+ SufamiTurboFilePromptMessage msg = Marshal.PtrToStructure(e.Parameter);
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ Dispatcher.UIThread.Post(async () => {
+ if(await MesenMsgBox.Show(this, "PromptLoadSufamiTurbo", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
+ string? selectedFile = await FileDialogHelper.OpenFile(null, this, FileDialogHelper.SufamiTurboExt);
+ if(selectedFile != null) {
+ byte[] file = Encoding.UTF8.GetBytes(selectedFile);
+ Array.Copy(file, msg.Filename, file.Length);
+ Marshal.StructureToPtr(msg, e.Parameter, false);
+ }
+ }
+ tcs.SetResult();
+ });
+ tcs.Task.Wait();
+ break;
+ }
case ConsoleNotificationType.BeforeGameLoad:
Dispatcher.UIThread.Post(() => {
diff --git a/Utilities/VirtualFile.cpp b/Utilities/VirtualFile.cpp
index ef0fa339..a8dfb8f3 100644
--- a/Utilities/VirtualFile.cpp
+++ b/Utilities/VirtualFile.cpp
@@ -13,7 +13,7 @@
const std::initializer_list VirtualFile::RomExtensions = {
".nes", ".fds", ".unif", ".unf", ".nsf", ".nsfe", ".studybox",
- ".sfc", ".swc", ".fig", ".smc", ".bs", ".spc",
+ ".sfc", ".swc", ".fig", ".smc", ".bs", ".st", ".spc",
".gb", ".gbc", ".gbx", ".gbs",
".pce", ".sgx", ".cue", ".hes",
".sms", ".gg", ".sg", ".col",