summaryrefslogtreecommitdiffstats
path: root/gearboy/src/GearboyCore.cpp
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2022-06-02 15:28:40 +0200
committerLouis Burda <quent.burda@gmail.com>2022-06-02 15:28:40 +0200
commit5bc16063c29aa4d3d287ebd163ccdbcbf54c4f9f (patch)
treec131f947a37b3af2d14d41e9eda098bdec2d061c /gearboy/src/GearboyCore.cpp
parent78a5f810b22f0d8cafa05f638b0cb2e889824859 (diff)
downloadcscg2022-gearboy-master.tar.gz
cscg2022-gearboy-master.zip
Added submodule filesHEADmaster
Diffstat (limited to 'gearboy/src/GearboyCore.cpp')
-rw-r--r--gearboy/src/GearboyCore.cpp1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/gearboy/src/GearboyCore.cpp b/gearboy/src/GearboyCore.cpp
new file mode 100644
index 00000000..5e3ff7e6
--- /dev/null
+++ b/gearboy/src/GearboyCore.cpp
@@ -0,0 +1,1002 @@
+/*
+ * Gearboy - Nintendo Game Boy Emulator
+ * Copyright (C) 2012 Ignacio Sanchez
+
+ * This program 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 3 of the License, or
+ * any later version.
+
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/
+ *
+ */
+
+#include "GearboyCore.h"
+#include "Memory.h"
+#include "Processor.h"
+#include "Video.h"
+#include "Audio.h"
+#include "Input.h"
+#include "Cartridge.h"
+#include "MemoryRule.h"
+#include "CommonMemoryRule.h"
+#include "IORegistersMemoryRule.h"
+#include "RomOnlyMemoryRule.h"
+#include "MBC1MemoryRule.h"
+#include "MBC2MemoryRule.h"
+#include "MBC3MemoryRule.h"
+#include "MBC5MemoryRule.h"
+#include "MultiMBC1MemoryRule.h"
+
+GearboyCore::GearboyCore()
+{
+ InitPointer(m_pMemory);
+ InitPointer(m_pProcessor);
+ InitPointer(m_pVideo);
+ InitPointer(m_pAudio);
+ InitPointer(m_pInput);
+ InitPointer(m_pCartridge);
+ InitPointer(m_pCommonMemoryRule);
+ InitPointer(m_pIORegistersMemoryRule);
+ InitPointer(m_pRomOnlyMemoryRule);
+ InitPointer(m_pMBC1MemoryRule);
+ InitPointer(m_pMultiMBC1MemoryRule);
+ InitPointer(m_pMBC2MemoryRule);
+ InitPointer(m_pMBC3MemoryRule);
+ InitPointer(m_pMBC5MemoryRule);
+ InitPointer(m_pRamChangedCallback);
+ m_bCGB = false;
+ m_bGBA = false;
+ m_bPaused = false;
+ m_bForceDMG = false;
+ m_iRTCUpdateCount = 0;
+ m_pixelFormat = GB_PIXEL_RGB565;
+}
+
+GearboyCore::~GearboyCore()
+{
+ SafeDelete(m_pMBC5MemoryRule);
+ SafeDelete(m_pMBC3MemoryRule);
+ SafeDelete(m_pMBC2MemoryRule);
+ SafeDelete(m_pMultiMBC1MemoryRule);
+ SafeDelete(m_pMBC1MemoryRule);
+ SafeDelete(m_pRomOnlyMemoryRule);
+ SafeDelete(m_pIORegistersMemoryRule);
+ SafeDelete(m_pCommonMemoryRule);
+ SafeDelete(m_pCartridge);
+ SafeDelete(m_pInput);
+ SafeDelete(m_pAudio);
+ SafeDelete(m_pVideo);
+ SafeDelete(m_pProcessor);
+ SafeDelete(m_pMemory);
+}
+
+void GearboyCore::Init(GB_Color_Format pixelFormat)
+{
+ Log("--== %s %s by Ignacio Sanchez ==--", GEARBOY_TITLE, GEARBOY_VERSION);
+
+ m_pixelFormat = pixelFormat;
+
+ m_pMemory = new Memory();
+ m_pProcessor = new Processor(m_pMemory);
+ m_pVideo = new Video(m_pMemory, m_pProcessor);
+ m_pAudio = new Audio();
+ m_pInput = new Input(m_pMemory, m_pProcessor);
+ m_pCartridge = new Cartridge();
+
+ m_pMemory->Init();
+ m_pProcessor->Init();
+ m_pVideo->Init();
+ m_pAudio->Init();
+ m_pInput->Init();
+ m_pCartridge->Init();
+
+ InitMemoryRules();
+ InitDMGPalette();
+}
+
+bool GearboyCore::RunToVBlank(u16* pFrameBuffer, s16* pSampleBuffer, int* pSampleCount, bool bDMGbuffer, bool step, bool stopOnBreakpoints)
+{
+ bool breakpoint = false;
+
+ if (!m_bPaused && m_pCartridge->IsLoadedROM())
+ {
+ bool vblank = false;
+ int totalClocks = 0;
+ while (!vblank)
+ {
+ #ifdef PERFORMANCE
+ unsigned int clockCycles = m_pProcessor->RunFor(75);
+ #else
+ unsigned int clockCycles = m_pProcessor->RunFor(1);
+ #endif
+
+ m_pProcessor->UpdateTimers(clockCycles);
+ m_pProcessor->UpdateSerial(clockCycles);
+
+ vblank = m_pVideo->Tick(clockCycles, pFrameBuffer, m_pixelFormat);
+ m_pAudio->Tick(clockCycles);
+ m_pInput->Tick(clockCycles);
+
+ totalClocks += clockCycles;
+
+#ifndef GEARBOY_DISABLE_DISASSEMBLER
+ if ((step || (stopOnBreakpoints && m_pProcessor->BreakpointHit())) && !m_pProcessor->Halted() && !m_pProcessor->DuringOpCode())
+ {
+ vblank = true;
+ if (m_pProcessor->BreakpointHit())
+ breakpoint = true;
+ }
+#endif
+
+ if (totalClocks > 702240)
+ vblank = true;
+ }
+
+ m_pAudio->EndFrame(pSampleBuffer, pSampleCount);
+
+ m_iRTCUpdateCount++;
+ if (m_iRTCUpdateCount == 20)
+ {
+ m_iRTCUpdateCount = 0;
+ m_pCartridge->UpdateCurrentRTC();
+ }
+
+ if (!m_bCGB && !bDMGbuffer)
+ {
+ RenderDMGFrame(pFrameBuffer);
+ }
+ }
+
+ return breakpoint;
+}
+
+bool GearboyCore::LoadROM(const char* szFilePath, bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
+{
+ if (m_pCartridge->LoadFromFile(szFilePath))
+ {
+ m_bForceDMG = forceDMG;
+ Reset(m_bForceDMG ? false : m_pCartridge->IsCGB(), forceGBA);
+ m_pMemory->ResetDisassembledMemory();
+ m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
+ bool romTypeOK = AddMemoryRules(forceType);
+#ifndef GEARBOY_DISABLE_DISASSEMBLER
+ m_pProcessor->Disassemble(m_pProcessor->GetState()->PC->GetValue());
+#endif
+
+ if (!romTypeOK)
+ {
+ Log("There was a problem with the cartridge header. File: %s...", szFilePath);
+ }
+
+ return romTypeOK;
+ }
+ else
+ return false;
+}
+
+bool GearboyCore::LoadROMFromBuffer(const u8* buffer, int size, bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
+{
+ if (m_pCartridge->LoadFromBuffer(buffer, size))
+ {
+ m_bForceDMG = forceDMG;
+ Reset(m_bForceDMG ? false : m_pCartridge->IsCGB(), forceGBA);
+ m_pMemory->ResetDisassembledMemory();
+ m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
+ bool romTypeOK = AddMemoryRules(forceType);
+
+ if (!romTypeOK)
+ {
+ Log("There was a problem with the cartridge header.");
+ }
+
+ return romTypeOK;
+ }
+ else
+ return false;
+}
+
+void GearboyCore::SaveMemoryDump()
+{
+ if (m_pCartridge->IsLoadedROM() && (strlen(m_pCartridge->GetFilePath()) > 0))
+ {
+ using namespace std;
+
+ char path[512];
+
+ strcpy(path, m_pCartridge->GetFilePath());
+ strcat(path, ".dump");
+
+ Log("Saving Memory Dump %s...", path);
+
+ m_pMemory->MemoryDump(path);
+
+ Log("Memory Dump Saved");
+ }
+}
+
+void GearboyCore::SaveDisassembledROM()
+{
+ Memory::stDisassembleRecord** romMap = m_pMemory->GetDisassembledROMMemoryMap();
+
+ if (m_pCartridge->IsLoadedROM() && (strlen(m_pCartridge->GetFilePath()) > 0) && IsValidPointer(romMap))
+ {
+ using namespace std;
+
+ char path[512];
+
+ strcpy(path, m_pCartridge->GetFilePath());
+ strcat(path, ".dis");
+
+ Log("Saving Disassembled ROM %s...", path);
+
+ ofstream myfile(path, ios::out | ios::trunc);
+
+ if (myfile.is_open())
+ {
+ for (int i = 0; i < 65536; i++)
+ {
+ if (IsValidPointer(romMap[i]) && (romMap[i]->name[0] != 0))
+ {
+ myfile << "0x" << hex << i << "\t " << romMap[i]->name << "\n";
+ i += (romMap[i]->size - 1);
+ }
+ }
+
+ myfile.close();
+ }
+
+ Log("Disassembled ROM Saved");
+ }
+}
+
+Memory* GearboyCore::GetMemory()
+{
+ return m_pMemory;
+}
+
+Cartridge* GearboyCore::GetCartridge()
+{
+ return m_pCartridge;
+}
+
+Processor* GearboyCore::GetProcessor()
+{
+ return m_pProcessor;
+}
+
+Audio* GearboyCore::GetAudio()
+{
+ return m_pAudio;
+}
+
+Video* GearboyCore::GetVideo()
+{
+ return m_pVideo;
+}
+
+void GearboyCore::KeyPressed(Gameboy_Keys key)
+{
+ m_pInput->KeyPressed(key);
+}
+
+void GearboyCore::KeyReleased(Gameboy_Keys key)
+{
+ m_pInput->KeyReleased(key);
+}
+
+void GearboyCore::Pause(bool paused)
+{
+ m_bPaused = paused;
+}
+
+bool GearboyCore::IsPaused()
+{
+ return m_bPaused;
+}
+
+void GearboyCore::ResetROM(bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
+{
+ if (m_pCartridge->IsLoadedROM())
+ {
+ m_bForceDMG = forceDMG;
+ Reset(m_bForceDMG ? false : m_pCartridge->IsCGB(), forceGBA);
+ m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
+ AddMemoryRules(forceType);
+#ifndef GEARBOY_DISABLE_DISASSEMBLER
+ m_pProcessor->Disassemble(m_pProcessor->GetState()->PC->GetValue());
+#endif
+ }
+}
+
+void GearboyCore::ResetROMPreservingRAM(bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
+{
+ if (m_pCartridge->IsLoadedROM())
+ {
+ Log("Resetting preserving RAM...");
+
+ using namespace std;
+ stringstream stream;
+
+ m_pMemory->GetCurrentRule()->SaveRam(stream);
+
+ ResetROM(forceDMG, forceType, forceGBA);
+
+ stream.seekg(0, stream.end);
+ s32 size = (s32)stream.tellg();
+ stream.seekg(0, stream.beg);
+
+ m_pMemory->GetCurrentRule()->LoadRam(stream, size);
+ }
+}
+
+void GearboyCore::ResetSound()
+{
+ m_pAudio->Reset(m_bCGB);
+}
+
+void GearboyCore::SetSoundSampleRate(int rate)
+{
+ m_pAudio->SetSampleRate(rate);
+}
+
+void GearboyCore::SetSoundVolume(float volume)
+{
+ m_pAudio->SetVolume(volume);
+}
+
+u16* GearboyCore::GetDMGInternalPalette()
+{
+ return m_DMGPalette;
+}
+
+void GearboyCore::SetDMGPalette(GB_Color& color1, GB_Color& color2, GB_Color& color3,
+ GB_Color& color4)
+{
+ bool format_565 = (m_pixelFormat == GB_PIXEL_RGB565) || (m_pixelFormat == GB_PIXEL_BGR565);
+ bool order_RGB = (m_pixelFormat == GB_PIXEL_RGB565) || (m_pixelFormat == GB_PIXEL_RGB555);
+
+ int multiplier = format_565 ? 63 : 31;
+ int shift = format_565 ? 11 : 10;
+
+ if (order_RGB)
+ {
+ m_DMGPalette[0] = (((color1.red * 31) / 255) << shift ) | (((color1.green * multiplier) / 255) << 5 ) | ((color1.blue * 31) / 255);
+ m_DMGPalette[1] = (((color2.red * 31) / 255) << shift ) | (((color2.green * multiplier) / 255) << 5 ) | ((color2.blue * 31) / 255);
+ m_DMGPalette[2] = (((color3.red * 31) / 255) << shift ) | (((color3.green * multiplier) / 255) << 5 ) | ((color3.blue * 31) / 255);
+ m_DMGPalette[3] = (((color4.red * 31) / 255) << shift ) | (((color4.green * multiplier) / 255) << 5 ) | ((color4.blue * 31) / 255);
+ }
+ else
+ {
+ m_DMGPalette[0] = (((color1.blue * 31) / 255) << shift ) | (((color1.green * multiplier) / 255) << 5 ) | ((color1.red * 31) / 255);
+ m_DMGPalette[1] = (((color2.blue * 31) / 255) << shift ) | (((color2.green * multiplier) / 255) << 5 ) | ((color2.red * 31) / 255);
+ m_DMGPalette[2] = (((color3.blue * 31) / 255) << shift ) | (((color3.green * multiplier) / 255) << 5 ) | ((color3.red * 31) / 255);
+ m_DMGPalette[3] = (((color4.blue * 31) / 255) << shift ) | (((color4.green * multiplier) / 255) << 5 ) | ((color4.red * 31) / 255);
+ }
+
+ if (!format_565)
+ {
+ m_DMGPalette[0] |= 0x8000;
+ m_DMGPalette[1] |= 0x8000;
+ m_DMGPalette[2] |= 0x8000;
+ m_DMGPalette[3] |= 0x8000;
+ }
+}
+
+void GearboyCore::SaveRam()
+{
+ SaveRam(NULL);
+}
+
+void GearboyCore::SaveRam(const char* szPath, bool fullPath)
+{
+ if (m_pCartridge->IsLoadedROM() && m_pCartridge->HasBattery() && IsValidPointer(m_pMemory->GetCurrentRule()))
+ {
+ Log("Saving RAM...");
+
+ using namespace std;
+
+ string path = "";
+
+ if (IsValidPointer(szPath))
+ {
+ path += szPath;
+
+ if (!fullPath)
+ {
+ path += "/";
+ path += m_pCartridge->GetFileName();
+ }
+ }
+ else
+ {
+ path = m_pCartridge->GetFilePath();
+ }
+
+ string::size_type i = path.rfind('.', path.length());
+
+ if (i != string::npos) {
+ path.replace(i + 1, 3, "sav");
+ }
+
+ Log("Save file: %s", path.c_str());
+
+ ofstream file(path.c_str(), ios::out | ios::binary);
+
+ m_pMemory->GetCurrentRule()->SaveRam(file);
+
+ Log("RAM saved");
+ }
+}
+
+void GearboyCore::LoadRam()
+{
+ LoadRam(NULL);
+}
+
+void GearboyCore::LoadRam(const char* szPath, bool fullPath)
+{
+ if (m_pCartridge->IsLoadedROM() && m_pCartridge->HasBattery() && IsValidPointer(m_pMemory->GetCurrentRule()))
+ {
+ Log("Loading RAM...");
+
+ using namespace std;
+
+ string sav_path = "";
+
+ if (IsValidPointer(szPath))
+ {
+ sav_path += szPath;
+
+ if (!fullPath)
+ {
+ sav_path += "/";
+ sav_path += m_pCartridge->GetFileName();
+ }
+ }
+ else
+ {
+ sav_path = m_pCartridge->GetFilePath();
+ }
+
+ string rom_path = sav_path;
+
+ string::size_type i = sav_path.rfind('.', sav_path.length());
+
+ if (i != string::npos) {
+ sav_path.replace(i + 1, 3, "sav");
+ }
+
+ Log("Opening save file: %s", sav_path.c_str());
+
+ ifstream file;
+
+ file.open(sav_path.c_str(), ios::in | ios::binary);
+
+ // check for old .gearboy saves
+ if (file.fail())
+ {
+ Log("Save file doesn't exist");
+ string old_sav_file = rom_path + ".gearboy";
+
+ Log("Opening old save file: %s", old_sav_file.c_str());
+ file.open(old_sav_file.c_str(), ios::in | ios::binary);
+ }
+
+ if (!file.fail())
+ {
+ file.seekg(0, file.end);
+ s32 fileSize = (s32)file.tellg();
+ file.seekg(0, file.beg);
+
+ if (m_pMemory->GetCurrentRule()->LoadRam(file, fileSize))
+ {
+ Log("RAM loaded");
+ }
+ else
+ {
+ Log("Save file size incorrect: %d", fileSize);
+ }
+ }
+ else
+ {
+ Log("Save file doesn't exist");
+ }
+ }
+}
+
+void GearboyCore::SaveState(int index)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return;
+ }
+
+ Log("Creating save state %d...", index);
+
+ SaveState(NULL, index);
+
+ Log("Save state %d created", index);
+}
+
+void GearboyCore::SaveState(const char* szPath, int index)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return;
+ }
+
+ Log("Saving state...");
+
+ using namespace std;
+
+ size_t size;
+ SaveState(NULL, size);
+
+ u8* buffer = new u8[size];
+ string path = "";
+
+ if (IsValidPointer(szPath))
+ {
+ path += szPath;
+ path += "/";
+ path += m_pCartridge->GetFileName();
+ }
+ else
+ {
+ path = m_pCartridge->GetFilePath();
+ }
+
+ string::size_type i = path.rfind('.', path.length());
+
+ if (i != string::npos) {
+ path.replace(i + 1, 3, "state");
+ }
+
+ std::stringstream sstm;
+
+ if (index < 0)
+ sstm << szPath;
+ else
+ sstm << path << index;
+
+ Log("Save state file: %s", sstm.str().c_str());
+
+ ofstream file(sstm.str().c_str(), ios::out | ios::binary);
+
+ SaveState(file, size);
+
+ SafeDeleteArray(buffer);
+
+ file.close();
+
+ Log("Save state created");
+}
+
+bool GearboyCore::SaveState(u8* buffer, size_t& size)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return false;
+ }
+
+ bool ret = false;
+
+ if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()))
+ {
+ using namespace std;
+
+ stringstream stream;
+
+ if (SaveState(stream, size))
+ ret = true;
+
+ if (IsValidPointer(buffer))
+ {
+ Log("Saving state to buffer [%d bytes]...", size);
+ memcpy(buffer, stream.str().c_str(), size);
+ ret = true;
+ }
+ }
+ else
+ {
+ Log("Invalid rom or memory rule.");
+ }
+
+ return ret;
+}
+
+bool GearboyCore::SaveState(std::ostream& stream, size_t& size)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return false;
+ }
+
+ if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()))
+ {
+ Log("Gathering save state data...");
+
+ using namespace std;
+
+ m_pMemory->SaveState(stream);
+ m_pProcessor->SaveState(stream);
+ m_pVideo->SaveState(stream);
+ m_pInput->SaveState(stream);
+ m_pAudio->SaveState(stream);
+ m_pMemory->GetCurrentRule()->SaveState(stream);
+
+ size = static_cast<size_t>(stream.tellp());
+
+ size += (sizeof(u32) * 2);
+
+ u32 header_magic = SAVESTATE_MAGIC;
+ u32 header_size = static_cast<u32>(size);
+
+ stream.write(reinterpret_cast<const char*> (&header_magic), sizeof(header_magic));
+ stream.write(reinterpret_cast<const char*> (&header_size), sizeof(header_size));
+
+ Log("Save state size: %d", static_cast<size_t>(stream.tellp()));
+
+ return true;
+ }
+
+ Log("Invalid rom or memory rule.");
+
+ return false;
+}
+
+void GearboyCore::LoadState(int index)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return;
+ }
+
+ Log("Loading save state %d...", index);
+
+ LoadState(NULL, index);
+
+ Log("State %d file loaded", index);
+}
+
+void GearboyCore::LoadState(const char* szPath, int index)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return;
+ }
+
+ Log("Loading save state...");
+
+ using namespace std;
+
+ string sav_path = "";
+
+ if (IsValidPointer(szPath))
+ {
+ sav_path += szPath;
+ sav_path += "/";
+ sav_path += m_pCartridge->GetFileName();
+ }
+ else
+ {
+ sav_path = m_pCartridge->GetFilePath();
+ }
+
+ string rom_path = sav_path;
+
+ string::size_type i = sav_path.rfind('.', sav_path.length());
+
+ if (i != string::npos) {
+ sav_path.replace(i + 1, 3, "state");
+ }
+
+ std::stringstream sstm;
+
+ if (index < 0)
+ sstm << szPath;
+ else
+ sstm << sav_path << index;
+
+ Log("Opening save file: %s", sstm.str().c_str());
+
+ ifstream file;
+
+ file.open(sstm.str().c_str(), ios::in | ios::binary);
+
+ if (!file.fail())
+ {
+ if (LoadState(file))
+ {
+ Log("Save state loaded");
+ }
+ }
+ else
+ {
+ Log("Save state file doesn't exist");
+ }
+
+ file.close();
+}
+
+bool GearboyCore::LoadState(const u8* buffer, size_t size)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return false;
+ }
+
+ if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()) && (size > 0) && IsValidPointer(buffer))
+ {
+ Log("Gathering load state data [%d bytes]...", size);
+
+ using namespace std;
+
+ stringstream stream;
+
+ stream.write(reinterpret_cast<const char*> (buffer), size);
+
+ return LoadState(stream);
+ }
+
+ Log("Invalid rom or memory rule.");
+
+ return false;
+}
+
+bool GearboyCore::LoadState(std::istream& stream)
+{
+ if (m_pMemory->IsBootromRegistryEnabled())
+ {
+ Log("Save states disabled when running bootrom");
+ return false;
+ }
+
+ if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()))
+ {
+ using namespace std;
+
+ u32 header_magic = 0;
+ u32 header_size = 0;
+
+ stream.seekg(0, ios::end);
+ size_t size = static_cast<size_t>(stream.tellg());
+
+ Log("Load state stream size: %d", size);
+
+ stream.seekg(size - (2 * sizeof(u32)), ios::beg);
+ stream.read(reinterpret_cast<char*> (&header_magic), sizeof(header_magic));
+ stream.read(reinterpret_cast<char*> (&header_size), sizeof(header_size));
+ stream.seekg(0, ios::beg);
+
+ Log("Load state magic: 0x%08x", header_magic);
+ Log("Load state size: %d", header_size);
+
+ if ((header_size == size) && (header_magic == SAVESTATE_MAGIC))
+ {
+ Log("Loading state...");
+
+ m_pMemory->LoadState(stream);
+ m_pProcessor->LoadState(stream);
+ m_pVideo->LoadState(stream);
+ m_pInput->LoadState(stream);
+ m_pAudio->LoadState(stream);
+ m_pMemory->GetCurrentRule()->LoadState(stream);
+
+ return true;
+ }
+ else
+ {
+ Log("Invalid save state size");
+ }
+ }
+ else
+ {
+ Log("Invalid rom or memory rule");
+ }
+
+ return false;
+}
+
+void GearboyCore::SetCheat(const char* szCheat)
+{
+ std::string s = szCheat;
+ if ((s.length() == 7) || (s.length() == 11))
+ {
+ m_pCartridge->SetGameGenieCheat(szCheat);
+ if (m_pCartridge->IsLoadedROM())
+ m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
+ }
+ else
+ {
+ m_pProcessor->SetGameSharkCheat(szCheat);
+ }
+}
+
+void GearboyCore::ClearCheats()
+{
+ m_pCartridge->ClearGameGenieCheats();
+ m_pProcessor->ClearGameSharkCheats();
+ if (m_pCartridge->IsLoadedROM())
+ m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
+}
+
+void GearboyCore::SetRamModificationCallback(RamChangedCallback callback)
+{
+ m_pRamChangedCallback = callback;
+}
+
+bool GearboyCore::IsCGB()
+{
+ return m_bCGB;
+}
+
+bool GearboyCore::IsGBA()
+{
+ return m_bGBA;
+}
+
+void GearboyCore::InitDMGPalette()
+{
+ GB_Color color[4];
+
+ color[0].red = 0x87;
+ color[0].green = 0x96;
+ color[0].blue = 0x03;
+
+ color[1].red = 0x4d;
+ color[1].green = 0x6b;
+ color[1].blue = 0x03;
+
+ color[2].red = 0x2b;
+ color[2].green = 0x55;
+ color[2].blue = 0x03;
+
+ color[3].red = 0x14;
+ color[3].green = 0x44;
+ color[3].blue = 0x03;
+
+ SetDMGPalette(color[0], color[1], color[2], color[3]);
+}
+
+void GearboyCore::InitMemoryRules()
+{
+ m_pIORegistersMemoryRule = new IORegistersMemoryRule(m_pProcessor, m_pMemory, m_pVideo, m_pInput, m_pAudio);
+
+ m_pCommonMemoryRule = new CommonMemoryRule(m_pMemory);
+
+ m_pRomOnlyMemoryRule = new RomOnlyMemoryRule(m_pProcessor, m_pMemory,
+ m_pVideo, m_pInput, m_pCartridge, m_pAudio);
+
+ m_pMBC1MemoryRule = new MBC1MemoryRule(m_pProcessor, m_pMemory,
+ m_pVideo, m_pInput, m_pCartridge, m_pAudio);
+
+ m_pMultiMBC1MemoryRule = new MultiMBC1MemoryRule(m_pProcessor, m_pMemory,
+ m_pVideo, m_pInput, m_pCartridge, m_pAudio);
+
+ m_pMBC2MemoryRule = new MBC2MemoryRule(m_pProcessor, m_pMemory,
+ m_pVideo, m_pInput, m_pCartridge, m_pAudio);
+
+ m_pMBC3MemoryRule = new MBC3MemoryRule(m_pProcessor, m_pMemory,
+ m_pVideo, m_pInput, m_pCartridge, m_pAudio);
+
+ m_pMBC5MemoryRule = new MBC5MemoryRule(m_pProcessor, m_pMemory,
+ m_pVideo, m_pInput, m_pCartridge, m_pAudio);
+
+ m_pMemory->SetCurrentRule(m_pRomOnlyMemoryRule);
+ m_pMemory->SetIORule(m_pIORegistersMemoryRule);
+ m_pMemory->SetCommonRule(m_pCommonMemoryRule);
+}
+
+bool GearboyCore::AddMemoryRules(Cartridge::CartridgeTypes forceType)
+{
+ m_pMemory->SetIORule(m_pIORegistersMemoryRule);
+ m_pMemory->SetCommonRule(m_pCommonMemoryRule);
+
+ Cartridge::CartridgeTypes type = m_pCartridge->GetType();
+
+ bool notSupported = false;
+
+ if (forceType != Cartridge::CartridgeNotSupported)
+ type = forceType;
+
+ switch (type)
+ {
+ case Cartridge::CartridgeNoMBC:
+ m_pMemory->SetCurrentRule(m_pRomOnlyMemoryRule);
+ break;
+ case Cartridge::CartridgeMBC1:
+ m_pMemory->SetCurrentRule(m_pMBC1MemoryRule);
+ break;
+ case Cartridge::CartridgeMBC1Multi:
+ m_pMemory->SetCurrentRule(m_pMultiMBC1MemoryRule);
+ break;
+ case Cartridge::CartridgeMBC2:
+ m_pMemory->SetCurrentRule(m_pMBC2MemoryRule);
+ break;
+ case Cartridge::CartridgeMBC3:
+ m_pMemory->SetCurrentRule(m_pMBC3MemoryRule);
+ break;
+ case Cartridge::CartridgeMBC5:
+ m_pMemory->SetCurrentRule(m_pMBC5MemoryRule);
+ break;
+ case Cartridge::CartridgeNotSupported:
+ notSupported = true;
+ break;
+ default:
+ notSupported = true;
+ }
+
+ if (!notSupported)
+ {
+ m_pMemory->GetCurrentRule()->SetRamChangedCallback(m_pRamChangedCallback);
+ }
+
+ return !notSupported;
+}
+
+void GearboyCore::Reset(bool bCGB, bool bGBA)
+{
+ m_bCGB = bCGB;
+ m_bGBA = bGBA;
+
+ if (m_bGBA && m_bCGB)
+ {
+ Log("Reset: Switching to Game Boy Advance");
+ }
+ else if (m_bCGB)
+ {
+ Log("Reset: Switching to Game Boy Color");
+ }
+ else
+ {
+ Log("Reset: Defaulting to Game Boy DMG");
+ }
+
+ m_pMemory->Reset(m_bCGB);
+ m_pProcessor->Reset(m_bCGB, m_bGBA);
+ m_pVideo->Reset(m_bCGB);
+ m_pAudio->Reset(m_bCGB);
+ m_pInput->Reset();
+ m_pCartridge->UpdateCurrentRTC();
+ m_iRTCUpdateCount = 0;
+
+ m_pCommonMemoryRule->Reset(m_bCGB);
+ m_pRomOnlyMemoryRule->Reset(m_bCGB);
+ m_pMBC1MemoryRule->Reset(m_bCGB);
+ m_pMultiMBC1MemoryRule->Reset(m_bCGB);
+ m_pMBC2MemoryRule->Reset(m_bCGB);
+ m_pMBC3MemoryRule->Reset(m_bCGB);
+ m_pMBC5MemoryRule->Reset(m_bCGB);
+ m_pIORegistersMemoryRule->Reset(m_bCGB);
+
+ m_bPaused = false;
+}
+
+void GearboyCore::RenderDMGFrame(u16* pFrameBuffer) const
+{
+ if (IsValidPointer(pFrameBuffer))
+ {
+ int pixels = GAMEBOY_WIDTH * GAMEBOY_HEIGHT;
+ const u8* pGameboyFrameBuffer = m_pVideo->GetFrameBuffer();
+
+ for (int i = 0; i < pixels; i++)
+ {
+ pFrameBuffer[i] = m_DMGPalette[pGameboyFrameBuffer[i]];
+ }
+ }
+}