Copy the logo data from the NDS ROM's header to the Boktai stub's header

This commit is contained in:
Jesse Talavera 2024-11-07 15:12:21 -05:00
parent e7e481f9e0
commit f6692dff8c
3 changed files with 102 additions and 1 deletions

View File

@ -539,6 +539,57 @@ CartGameSolarSensor::CartGameSolarSensor(std::unique_ptr<u8[]>&& rom, u32 len, s
{ {
} }
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata) noexcept
{
return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata);
}
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata) noexcept
{
return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata);
}
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata) noexcept
{
if (!gamecode)
return nullptr;
if (strnlen(gamecode, sizeof(GBAHeader::GameCode)) > sizeof(GBAHeader::GameCode))
return nullptr;
bool solarsensor = false;
for (const char* i : SOLAR_SENSOR_GAMECODES)
{
if (strcmp(gamecode, i) == 0) {
solarsensor = true;
break;
}
}
if (!solarsensor)
return nullptr;
// just 256 bytes; we don't need a whole ROM!
constexpr size_t FAKE_BOKTAI_ROM_LENGTH = 0x100;
std::unique_ptr<u8[]> rom = std::make_unique<u8[]>(FAKE_BOKTAI_ROM_LENGTH);
// create a fake ROM
GBAHeader& header = *reinterpret_cast<GBAHeader*>(rom.get());
memcpy(header.Title, BOKTAI_STUB_TITLE, strnlen(BOKTAI_STUB_TITLE, sizeof(header.Title)));
memcpy(header.GameCode, gamecode, strnlen(gamecode, sizeof(header.GameCode)));
header.FixedValue = 0x96;
if (logo)
{
memcpy(header.NintendoLogo, logo, sizeof(header.NintendoLogo));
}
else
{
memset(header.NintendoLogo, 0xFF, sizeof(header.NintendoLogo));
}
return std::make_unique<CartGameSolarSensor>(std::move(rom), FAKE_BOKTAI_ROM_LENGTH, nullptr, 0, userdata);
}
const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183}; const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183};
void CartGameSolarSensor::Reset() void CartGameSolarSensor::Reset()
@ -874,6 +925,18 @@ void GBACartSlot::LoadAddon(void* userdata, int type) noexcept
case GBAAddon_RumblePak: case GBAAddon_RumblePak:
Cart = std::make_unique<CartRumblePak>(userdata); Cart = std::make_unique<CartRumblePak>(userdata);
break; break;
case GBAAddon_SolarSensorBoktai1:
// US Boktai 1
Cart = CreateFakeSolarSensorROM("U3IE", nullptr, userdata);
break;
case GBAAddon_SolarSensorBoktai2:
// US Boktai 2
Cart = CreateFakeSolarSensorROM("U32E", nullptr, userdata);
break;
case GBAAddon_SolarSensorBoktai3:
// JP Boktai 3
Cart = CreateFakeSolarSensorROM("U33J", nullptr, userdata);
break;
default: default:
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);

View File

@ -39,7 +39,7 @@ enum CartType
struct GBAHeader struct GBAHeader
{ {
u32 EntryPoint; u32 EntryPoint;
u8 NintendoLogo[156]; u8 NintendoLogo[156]; // must be valid
char Title[12]; char Title[12];
char GameCode[4]; char GameCode[4];
char MakerCode[2]; char MakerCode[2];
@ -111,6 +111,7 @@ public:
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); } [[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; } [[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
[[nodiscard]] const GBAHeader& GetHeader() const noexcept { return *reinterpret_cast<const GBAHeader*>(ROM.get()); } [[nodiscard]] const GBAHeader& GetHeader() const noexcept { return *reinterpret_cast<const GBAHeader*>(ROM.get()); }
[[nodiscard]] GBAHeader& GetHeader() noexcept { return *reinterpret_cast<GBAHeader*>(ROM.get()); }
u8* GetSaveMemory() const override; u8* GetSaveMemory() const override;
u32 GetSaveMemoryLength() const override; u32 GetSaveMemoryLength() const override;
@ -329,6 +330,23 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, const u8* sr
/// or \c nullptr if there was an error. /// or \c nullptr if there was an error.
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen, void* userdata = nullptr); std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen, void* userdata = nullptr);
/// Creates a solar sensor-enabled GBA cart without needing a real Boktai ROM.
/// This enables the solar sensor to be used in supported games.
/// Will not contain any SRAM.
/// @param gamecode
/// @param logo The Nintendo logo data embedded in the headers for GBA ROMs and NDS ROMs.
/// Required for the cart to be recognized as a valid GBA cart.
/// Overloads that accept cart objects directly exist as well.
/// If not provided, then it will have to be patched with equivalent data
/// from a real ROM (NDS or GBA) before booting the emulator.
/// @param userdata Optional user data to associate with the cart.
/// @return A CartGameSolarSensor if the ROM was created successfully,
/// or nullptr if any argument is wrong (e.g. an incorrect game code).
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata = nullptr) noexcept;
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata = nullptr) noexcept;
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata = nullptr) noexcept;
constexpr const char* BOKTAI_STUB_TITLE = "BOKTAI STUB";
} }
#endif // GBACART_H #endif // GBACART_H

View File

@ -539,6 +539,26 @@ void NDS::Reset()
void NDS::Start() void NDS::Start()
{ {
Running = true; Running = true;
if (ConsoleType != 0)
return;
auto* ndscart = NDSCartSlot.GetCart();
if (!ndscart)
return;
if (auto* cart = GBACartSlot.GetCart(); cart && cart->Type() == GBACart::CartType::GameSolarSensor)
{ // If we have a solar sensor cart inserted...
auto& solarcart = *static_cast<GBACart::CartGameSolarSensor*>(cart);
GBACart::GBAHeader& header = solarcart.GetHeader();
if (strncmp(header.Title, GBACart::BOKTAI_STUB_TITLE, sizeof(header.Title)) == 0) {
// If this is a stub Boktai cart (so we can use the sensor without a full ROM)...
// ...then copy the Nintendo logo data from the NDS ROM into the stub GBA ROM.
// Otherwise, the GBA cart won't be recognized.
memcpy(header.NintendoLogo, ndscart->GetHeader().NintendoLogo, sizeof(header.NintendoLogo));
}
}
} }
static const char* StopReasonName(Platform::StopReason reason) static const char* StopReasonName(Platform::StopReason reason)