cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

GearboyCore.cpp (25505B)


      1/*
      2 * Gearboy - Nintendo Game Boy Emulator
      3 * Copyright (C) 2012  Ignacio Sanchez
      4
      5 * This program is free software: you can redistribute it and/or modify
      6 * it under the terms of the GNU General Public License as published by
      7 * the Free Software Foundation, either version 3 of the License, or
      8 * any later version.
      9
     10 * This program is distributed in the hope that it will be useful,
     11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     13 * GNU General Public License for more details.
     14
     15 * You should have received a copy of the GNU General Public License
     16 * along with this program.  If not, see http://www.gnu.org/licenses/
     17 *
     18 */
     19
     20#include "GearboyCore.h"
     21#include "Memory.h"
     22#include "Processor.h"
     23#include "Video.h"
     24#include "Audio.h"
     25#include "Input.h"
     26#include "Cartridge.h"
     27#include "MemoryRule.h"
     28#include "CommonMemoryRule.h"
     29#include "IORegistersMemoryRule.h"
     30#include "RomOnlyMemoryRule.h"
     31#include "MBC1MemoryRule.h"
     32#include "MBC2MemoryRule.h"
     33#include "MBC3MemoryRule.h"
     34#include "MBC5MemoryRule.h"
     35#include "MultiMBC1MemoryRule.h"
     36
     37GearboyCore::GearboyCore()
     38{
     39    InitPointer(m_pMemory);
     40    InitPointer(m_pProcessor);
     41    InitPointer(m_pVideo);
     42    InitPointer(m_pAudio);
     43    InitPointer(m_pInput);
     44    InitPointer(m_pCartridge);
     45    InitPointer(m_pCommonMemoryRule);
     46    InitPointer(m_pIORegistersMemoryRule);
     47    InitPointer(m_pRomOnlyMemoryRule);
     48    InitPointer(m_pMBC1MemoryRule);
     49    InitPointer(m_pMultiMBC1MemoryRule);
     50    InitPointer(m_pMBC2MemoryRule);
     51    InitPointer(m_pMBC3MemoryRule);
     52    InitPointer(m_pMBC5MemoryRule);
     53    InitPointer(m_pRamChangedCallback);
     54    m_bCGB = false;
     55    m_bGBA = false;
     56    m_bPaused = false;
     57    m_bForceDMG = false;
     58    m_iRTCUpdateCount = 0;
     59    m_pixelFormat = GB_PIXEL_RGB565;
     60}
     61
     62GearboyCore::~GearboyCore()
     63{
     64    SafeDelete(m_pMBC5MemoryRule);
     65    SafeDelete(m_pMBC3MemoryRule);
     66    SafeDelete(m_pMBC2MemoryRule);
     67    SafeDelete(m_pMultiMBC1MemoryRule);
     68    SafeDelete(m_pMBC1MemoryRule);
     69    SafeDelete(m_pRomOnlyMemoryRule);
     70    SafeDelete(m_pIORegistersMemoryRule);
     71    SafeDelete(m_pCommonMemoryRule);
     72    SafeDelete(m_pCartridge);
     73    SafeDelete(m_pInput);
     74    SafeDelete(m_pAudio);
     75    SafeDelete(m_pVideo);
     76    SafeDelete(m_pProcessor);
     77    SafeDelete(m_pMemory);
     78}
     79
     80void GearboyCore::Init(GB_Color_Format pixelFormat)
     81{
     82    Log("--== %s %s by Ignacio Sanchez ==--", GEARBOY_TITLE, GEARBOY_VERSION);
     83
     84    m_pixelFormat = pixelFormat;
     85
     86    m_pMemory = new Memory();
     87    m_pProcessor = new Processor(m_pMemory);
     88    m_pVideo = new Video(m_pMemory, m_pProcessor);
     89    m_pAudio = new Audio();
     90    m_pInput = new Input(m_pMemory, m_pProcessor);
     91    m_pCartridge = new Cartridge();
     92
     93    m_pMemory->Init();
     94    m_pProcessor->Init();
     95    m_pVideo->Init();
     96    m_pAudio->Init();
     97    m_pInput->Init();
     98    m_pCartridge->Init();
     99
    100    InitMemoryRules();
    101    InitDMGPalette();
    102}
    103
    104bool GearboyCore::RunToVBlank(u16* pFrameBuffer, s16* pSampleBuffer, int* pSampleCount, bool bDMGbuffer, bool step, bool stopOnBreakpoints)
    105{
    106    bool breakpoint = false;
    107
    108    if (!m_bPaused && m_pCartridge->IsLoadedROM())
    109    {
    110        bool vblank = false;
    111        int totalClocks = 0;
    112        while (!vblank)
    113        {
    114            #ifdef PERFORMANCE
    115                unsigned int clockCycles = m_pProcessor->RunFor(75);
    116            #else
    117                unsigned int clockCycles = m_pProcessor->RunFor(1);
    118            #endif
    119
    120            m_pProcessor->UpdateTimers(clockCycles);
    121            m_pProcessor->UpdateSerial(clockCycles);
    122            
    123            vblank = m_pVideo->Tick(clockCycles, pFrameBuffer, m_pixelFormat);
    124            m_pAudio->Tick(clockCycles);
    125            m_pInput->Tick(clockCycles);
    126
    127            totalClocks += clockCycles;
    128
    129#ifndef GEARBOY_DISABLE_DISASSEMBLER
    130            if ((step || (stopOnBreakpoints && m_pProcessor->BreakpointHit())) && !m_pProcessor->Halted() && !m_pProcessor->DuringOpCode())
    131            {
    132                vblank = true;
    133                if (m_pProcessor->BreakpointHit())
    134                    breakpoint = true;
    135            }
    136#endif
    137
    138            if (totalClocks > 702240)
    139                vblank = true;
    140        }
    141
    142        m_pAudio->EndFrame(pSampleBuffer, pSampleCount);
    143
    144        m_iRTCUpdateCount++;
    145        if (m_iRTCUpdateCount == 20)
    146        {
    147            m_iRTCUpdateCount = 0;
    148            m_pCartridge->UpdateCurrentRTC();
    149        }
    150
    151        if (!m_bCGB && !bDMGbuffer)
    152        {
    153            RenderDMGFrame(pFrameBuffer);
    154        }
    155    }
    156
    157    return breakpoint;
    158}
    159
    160bool GearboyCore::LoadROM(const char* szFilePath, bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
    161{
    162    if (m_pCartridge->LoadFromFile(szFilePath))
    163    {
    164        m_bForceDMG = forceDMG;
    165        Reset(m_bForceDMG ? false : m_pCartridge->IsCGB(), forceGBA);
    166        m_pMemory->ResetDisassembledMemory();
    167        m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
    168        bool romTypeOK = AddMemoryRules(forceType);
    169#ifndef GEARBOY_DISABLE_DISASSEMBLER
    170        m_pProcessor->Disassemble(m_pProcessor->GetState()->PC->GetValue());
    171#endif
    172
    173        if (!romTypeOK)
    174        {
    175            Log("There was a problem with the cartridge header. File: %s...", szFilePath);
    176        }
    177
    178        return romTypeOK;
    179    }
    180    else
    181        return false;
    182}
    183
    184bool GearboyCore::LoadROMFromBuffer(const u8* buffer, int size, bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
    185{
    186    if (m_pCartridge->LoadFromBuffer(buffer, size))
    187    {
    188        m_bForceDMG = forceDMG;
    189        Reset(m_bForceDMG ? false : m_pCartridge->IsCGB(), forceGBA);
    190        m_pMemory->ResetDisassembledMemory();
    191        m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
    192        bool romTypeOK = AddMemoryRules(forceType);
    193
    194        if (!romTypeOK)
    195        {
    196            Log("There was a problem with the cartridge header.");
    197        }
    198
    199        return romTypeOK;
    200    }
    201    else
    202        return false;
    203}
    204
    205void GearboyCore::SaveMemoryDump()
    206{
    207    if (m_pCartridge->IsLoadedROM() && (strlen(m_pCartridge->GetFilePath()) > 0))
    208    {
    209        using namespace std;
    210
    211        char path[512];
    212
    213        strcpy(path, m_pCartridge->GetFilePath());
    214        strcat(path, ".dump");
    215
    216        Log("Saving Memory Dump %s...", path);
    217
    218        m_pMemory->MemoryDump(path);
    219
    220        Log("Memory Dump Saved");
    221    }
    222}
    223
    224void GearboyCore::SaveDisassembledROM()
    225{
    226    Memory::stDisassembleRecord** romMap = m_pMemory->GetDisassembledROMMemoryMap();
    227
    228    if (m_pCartridge->IsLoadedROM() && (strlen(m_pCartridge->GetFilePath()) > 0) && IsValidPointer(romMap))
    229    {
    230        using namespace std;
    231
    232        char path[512];
    233
    234        strcpy(path, m_pCartridge->GetFilePath());
    235        strcat(path, ".dis");
    236
    237        Log("Saving Disassembled ROM %s...", path);
    238
    239        ofstream myfile(path, ios::out | ios::trunc);
    240
    241        if (myfile.is_open())
    242        {
    243            for (int i = 0; i < 65536; i++)
    244            {
    245                if (IsValidPointer(romMap[i]) && (romMap[i]->name[0] != 0))
    246                {
    247                    myfile << "0x" << hex << i << "\t " << romMap[i]->name << "\n";
    248                    i += (romMap[i]->size - 1);
    249                }
    250            }
    251
    252            myfile.close();
    253        }
    254
    255        Log("Disassembled ROM Saved");
    256    }
    257}
    258
    259Memory* GearboyCore::GetMemory()
    260{
    261    return m_pMemory;
    262}
    263
    264Cartridge* GearboyCore::GetCartridge()
    265{
    266    return m_pCartridge;
    267}
    268
    269Processor* GearboyCore::GetProcessor()
    270{
    271    return m_pProcessor;
    272}
    273
    274Audio* GearboyCore::GetAudio()
    275{
    276    return m_pAudio;
    277}
    278
    279Video* GearboyCore::GetVideo()
    280{
    281    return m_pVideo;
    282}
    283
    284void GearboyCore::KeyPressed(Gameboy_Keys key)
    285{
    286    m_pInput->KeyPressed(key);
    287}
    288
    289void GearboyCore::KeyReleased(Gameboy_Keys key)
    290{
    291    m_pInput->KeyReleased(key);
    292}
    293
    294void GearboyCore::Pause(bool paused)
    295{
    296    m_bPaused = paused;
    297}
    298
    299bool GearboyCore::IsPaused()
    300{
    301    return m_bPaused;
    302}
    303
    304void GearboyCore::ResetROM(bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
    305{
    306    if (m_pCartridge->IsLoadedROM())
    307    {
    308        m_bForceDMG = forceDMG;
    309        Reset(m_bForceDMG ? false : m_pCartridge->IsCGB(), forceGBA);
    310        m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
    311        AddMemoryRules(forceType);
    312#ifndef GEARBOY_DISABLE_DISASSEMBLER
    313        m_pProcessor->Disassemble(m_pProcessor->GetState()->PC->GetValue());
    314#endif
    315    }
    316}
    317
    318void GearboyCore::ResetROMPreservingRAM(bool forceDMG, Cartridge::CartridgeTypes forceType, bool forceGBA)
    319{
    320    if (m_pCartridge->IsLoadedROM())
    321    {
    322        Log("Resetting preserving RAM...");
    323
    324        using namespace std;
    325        stringstream stream;
    326
    327        m_pMemory->GetCurrentRule()->SaveRam(stream);
    328
    329        ResetROM(forceDMG, forceType, forceGBA);
    330
    331        stream.seekg(0, stream.end);
    332        s32 size = (s32)stream.tellg();
    333        stream.seekg(0, stream.beg);
    334
    335        m_pMemory->GetCurrentRule()->LoadRam(stream, size);
    336    }
    337}
    338
    339void GearboyCore::ResetSound()
    340{
    341    m_pAudio->Reset(m_bCGB);
    342}
    343
    344void GearboyCore::SetSoundSampleRate(int rate)
    345{
    346    m_pAudio->SetSampleRate(rate);
    347}
    348
    349void GearboyCore::SetSoundVolume(float volume)
    350{
    351    m_pAudio->SetVolume(volume);
    352}
    353
    354u16* GearboyCore::GetDMGInternalPalette()
    355{
    356    return m_DMGPalette;
    357}
    358
    359void GearboyCore::SetDMGPalette(GB_Color& color1, GB_Color& color2, GB_Color& color3,
    360        GB_Color& color4)
    361{
    362    bool format_565 = (m_pixelFormat == GB_PIXEL_RGB565) || (m_pixelFormat == GB_PIXEL_BGR565);
    363    bool order_RGB = (m_pixelFormat == GB_PIXEL_RGB565) || (m_pixelFormat == GB_PIXEL_RGB555);
    364
    365    int multiplier = format_565 ? 63 : 31;
    366    int shift = format_565 ? 11 : 10;
    367
    368    if (order_RGB)
    369    {
    370        m_DMGPalette[0] = (((color1.red * 31) / 255) << shift ) | (((color1.green * multiplier) / 255) << 5 ) | ((color1.blue * 31) / 255);
    371        m_DMGPalette[1] = (((color2.red * 31) / 255) << shift ) | (((color2.green * multiplier) / 255) << 5 ) | ((color2.blue * 31) / 255);
    372        m_DMGPalette[2] = (((color3.red * 31) / 255) << shift ) | (((color3.green * multiplier) / 255) << 5 ) | ((color3.blue * 31) / 255);
    373        m_DMGPalette[3] = (((color4.red * 31) / 255) << shift ) | (((color4.green * multiplier) / 255) << 5 ) | ((color4.blue * 31) / 255);        
    374    }
    375    else
    376    {
    377        m_DMGPalette[0] = (((color1.blue * 31) / 255) << shift ) | (((color1.green * multiplier) / 255) << 5 ) | ((color1.red * 31) / 255);
    378        m_DMGPalette[1] = (((color2.blue * 31) / 255) << shift ) | (((color2.green * multiplier) / 255) << 5 ) | ((color2.red * 31) / 255);
    379        m_DMGPalette[2] = (((color3.blue * 31) / 255) << shift ) | (((color3.green * multiplier) / 255) << 5 ) | ((color3.red * 31) / 255);
    380        m_DMGPalette[3] = (((color4.blue * 31) / 255) << shift ) | (((color4.green * multiplier) / 255) << 5 ) | ((color4.red * 31) / 255);
    381    }   
    382
    383    if (!format_565)
    384    {
    385        m_DMGPalette[0] |= 0x8000;
    386        m_DMGPalette[1] |= 0x8000;
    387        m_DMGPalette[2] |= 0x8000;
    388        m_DMGPalette[3] |= 0x8000;
    389    }
    390}
    391
    392void GearboyCore::SaveRam()
    393{
    394    SaveRam(NULL);
    395}
    396
    397void GearboyCore::SaveRam(const char* szPath, bool fullPath)
    398{
    399    if (m_pCartridge->IsLoadedROM() && m_pCartridge->HasBattery() && IsValidPointer(m_pMemory->GetCurrentRule()))
    400    {
    401        Log("Saving RAM...");
    402
    403        using namespace std;
    404
    405        string path = "";
    406
    407        if (IsValidPointer(szPath))
    408        {
    409            path += szPath;
    410
    411            if (!fullPath)
    412            {
    413                path += "/";
    414                path += m_pCartridge->GetFileName();
    415            }
    416        }
    417        else
    418        {
    419            path = m_pCartridge->GetFilePath();
    420        }
    421
    422        string::size_type i = path.rfind('.', path.length());
    423
    424        if (i != string::npos) {
    425            path.replace(i + 1, 3, "sav");
    426        }
    427
    428        Log("Save file: %s", path.c_str());
    429
    430        ofstream file(path.c_str(), ios::out | ios::binary);
    431
    432        m_pMemory->GetCurrentRule()->SaveRam(file);
    433
    434        Log("RAM saved");
    435    }
    436}
    437
    438void GearboyCore::LoadRam()
    439{
    440    LoadRam(NULL);
    441}
    442
    443void GearboyCore::LoadRam(const char* szPath, bool fullPath)
    444{
    445    if (m_pCartridge->IsLoadedROM() && m_pCartridge->HasBattery() && IsValidPointer(m_pMemory->GetCurrentRule()))
    446    {
    447        Log("Loading RAM...");
    448
    449        using namespace std;
    450
    451        string sav_path = "";
    452
    453        if (IsValidPointer(szPath))
    454        {
    455            sav_path += szPath;
    456
    457            if (!fullPath)
    458            {
    459                sav_path += "/";
    460                sav_path += m_pCartridge->GetFileName();
    461            }
    462        }
    463        else
    464        {
    465            sav_path = m_pCartridge->GetFilePath();
    466        }
    467
    468        string rom_path = sav_path;
    469
    470        string::size_type i = sav_path.rfind('.', sav_path.length());
    471
    472        if (i != string::npos) {
    473            sav_path.replace(i + 1, 3, "sav");
    474        }
    475
    476        Log("Opening save file: %s", sav_path.c_str());
    477
    478        ifstream file;
    479
    480        file.open(sav_path.c_str(), ios::in | ios::binary);
    481
    482        // check for old .gearboy saves
    483        if (file.fail())
    484        {
    485            Log("Save file doesn't exist");
    486            string old_sav_file = rom_path + ".gearboy";
    487
    488            Log("Opening old save file: %s", old_sav_file.c_str());
    489            file.open(old_sav_file.c_str(), ios::in | ios::binary);
    490        }
    491
    492        if (!file.fail())
    493        {
    494            file.seekg(0, file.end);
    495            s32 fileSize = (s32)file.tellg();
    496            file.seekg(0, file.beg);
    497
    498            if (m_pMemory->GetCurrentRule()->LoadRam(file, fileSize))
    499            {
    500                Log("RAM loaded");
    501            }
    502            else
    503            {
    504                Log("Save file size incorrect: %d", fileSize);
    505            }
    506        }
    507        else
    508        {
    509            Log("Save file doesn't exist");
    510        }
    511    }
    512}
    513
    514void GearboyCore::SaveState(int index)
    515{
    516    if (m_pMemory->IsBootromRegistryEnabled())
    517    {
    518        Log("Save states disabled when running bootrom");
    519        return;
    520    }
    521
    522    Log("Creating save state %d...", index);
    523
    524    SaveState(NULL, index);
    525
    526    Log("Save state %d created", index);
    527}
    528
    529void GearboyCore::SaveState(const char* szPath, int index)
    530{
    531    if (m_pMemory->IsBootromRegistryEnabled())
    532    {
    533        Log("Save states disabled when running bootrom");
    534        return;
    535    }
    536
    537    Log("Saving state...");
    538
    539    using namespace std;
    540
    541    size_t size;
    542    SaveState(NULL, size);
    543
    544    u8* buffer = new u8[size];
    545    string path = "";
    546
    547    if (IsValidPointer(szPath))
    548    {
    549        path += szPath;
    550        path += "/";
    551        path += m_pCartridge->GetFileName();
    552    }
    553    else
    554    {
    555        path = m_pCartridge->GetFilePath();
    556    }
    557
    558    string::size_type i = path.rfind('.', path.length());
    559
    560    if (i != string::npos) {
    561        path.replace(i + 1, 3, "state");
    562    }
    563
    564    std::stringstream sstm;
    565
    566    if (index < 0)
    567        sstm << szPath;
    568    else
    569        sstm << path << index;
    570
    571    Log("Save state file: %s", sstm.str().c_str());
    572
    573    ofstream file(sstm.str().c_str(), ios::out | ios::binary);
    574
    575    SaveState(file, size);
    576
    577    SafeDeleteArray(buffer);
    578
    579    file.close();
    580
    581    Log("Save state created");
    582}
    583
    584bool GearboyCore::SaveState(u8* buffer, size_t& size)
    585{
    586    if (m_pMemory->IsBootromRegistryEnabled())
    587    {
    588        Log("Save states disabled when running bootrom");
    589        return false;
    590    }
    591
    592    bool ret = false;
    593
    594    if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()))
    595    {
    596        using namespace std;
    597
    598        stringstream stream;
    599
    600        if (SaveState(stream, size))
    601            ret = true;
    602
    603        if (IsValidPointer(buffer))
    604        {
    605            Log("Saving state to buffer [%d bytes]...", size);
    606            memcpy(buffer, stream.str().c_str(), size);
    607            ret = true;
    608        }
    609    }
    610    else
    611    {
    612        Log("Invalid rom or memory rule.");
    613    }
    614
    615    return ret;
    616}
    617
    618bool GearboyCore::SaveState(std::ostream& stream, size_t& size)
    619{
    620    if (m_pMemory->IsBootromRegistryEnabled())
    621    {
    622        Log("Save states disabled when running bootrom");
    623        return false;
    624    }
    625
    626    if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()))
    627    {
    628        Log("Gathering save state data...");
    629
    630        using namespace std;
    631
    632        m_pMemory->SaveState(stream);
    633        m_pProcessor->SaveState(stream);
    634        m_pVideo->SaveState(stream);
    635        m_pInput->SaveState(stream);
    636        m_pAudio->SaveState(stream);
    637        m_pMemory->GetCurrentRule()->SaveState(stream);
    638
    639        size = static_cast<size_t>(stream.tellp());
    640
    641        size += (sizeof(u32) * 2);
    642
    643        u32 header_magic = SAVESTATE_MAGIC;
    644        u32 header_size = static_cast<u32>(size);
    645
    646        stream.write(reinterpret_cast<const char*> (&header_magic), sizeof(header_magic));
    647        stream.write(reinterpret_cast<const char*> (&header_size), sizeof(header_size));
    648
    649        Log("Save state size: %d", static_cast<size_t>(stream.tellp()));
    650
    651        return true;
    652    }
    653
    654    Log("Invalid rom or memory rule.");
    655
    656    return false;
    657}
    658
    659void GearboyCore::LoadState(int index)
    660{
    661    if (m_pMemory->IsBootromRegistryEnabled())
    662    {
    663        Log("Save states disabled when running bootrom");
    664        return;
    665    }
    666
    667    Log("Loading save state %d...", index);
    668
    669    LoadState(NULL, index);
    670
    671    Log("State %d file loaded", index);
    672}
    673
    674void GearboyCore::LoadState(const char* szPath, int index)
    675{
    676    if (m_pMemory->IsBootromRegistryEnabled())
    677    {
    678        Log("Save states disabled when running bootrom");
    679        return;
    680    }
    681
    682    Log("Loading save state...");
    683
    684    using namespace std;
    685
    686    string sav_path = "";
    687
    688    if (IsValidPointer(szPath))
    689    {
    690        sav_path += szPath;
    691        sav_path += "/";
    692        sav_path += m_pCartridge->GetFileName();
    693    }
    694    else
    695    {
    696        sav_path = m_pCartridge->GetFilePath();
    697    }
    698
    699    string rom_path = sav_path;
    700
    701    string::size_type i = sav_path.rfind('.', sav_path.length());
    702
    703    if (i != string::npos) {
    704        sav_path.replace(i + 1, 3, "state");
    705    }
    706
    707    std::stringstream sstm;
    708
    709    if (index < 0)
    710        sstm << szPath;
    711    else
    712        sstm << sav_path << index;
    713
    714    Log("Opening save file: %s", sstm.str().c_str());
    715
    716    ifstream file;
    717
    718    file.open(sstm.str().c_str(), ios::in | ios::binary);
    719
    720    if (!file.fail())
    721    {
    722        if (LoadState(file))
    723        {
    724            Log("Save state loaded");
    725        }
    726    }
    727    else
    728    {
    729        Log("Save state file doesn't exist");
    730    }
    731
    732    file.close();
    733}
    734
    735bool GearboyCore::LoadState(const u8* buffer, size_t size)
    736{
    737    if (m_pMemory->IsBootromRegistryEnabled())
    738    {
    739        Log("Save states disabled when running bootrom");
    740        return false;
    741    }
    742
    743    if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()) && (size > 0) && IsValidPointer(buffer))
    744    {
    745        Log("Gathering load state data [%d bytes]...", size);
    746
    747        using namespace std;
    748
    749        stringstream stream;
    750
    751        stream.write(reinterpret_cast<const char*> (buffer), size);
    752
    753        return LoadState(stream);
    754    }
    755
    756    Log("Invalid rom or memory rule.");
    757
    758    return false;
    759}
    760
    761bool GearboyCore::LoadState(std::istream& stream)
    762{
    763    if (m_pMemory->IsBootromRegistryEnabled())
    764    {
    765        Log("Save states disabled when running bootrom");
    766        return false;
    767    }
    768
    769    if (m_pCartridge->IsLoadedROM() && IsValidPointer(m_pMemory->GetCurrentRule()))
    770    {
    771        using namespace std;
    772
    773        u32 header_magic = 0;
    774        u32 header_size = 0;
    775
    776        stream.seekg(0, ios::end);
    777        size_t size = static_cast<size_t>(stream.tellg());
    778
    779        Log("Load state stream size: %d", size);
    780
    781        stream.seekg(size - (2 * sizeof(u32)), ios::beg);
    782        stream.read(reinterpret_cast<char*> (&header_magic), sizeof(header_magic));
    783        stream.read(reinterpret_cast<char*> (&header_size), sizeof(header_size));
    784        stream.seekg(0, ios::beg);
    785
    786        Log("Load state magic: 0x%08x", header_magic);
    787        Log("Load state size: %d", header_size);
    788
    789        if ((header_size == size) && (header_magic == SAVESTATE_MAGIC))
    790        {
    791            Log("Loading state...");
    792
    793            m_pMemory->LoadState(stream);
    794            m_pProcessor->LoadState(stream);
    795            m_pVideo->LoadState(stream);
    796            m_pInput->LoadState(stream);
    797            m_pAudio->LoadState(stream);
    798            m_pMemory->GetCurrentRule()->LoadState(stream);
    799
    800            return true;
    801        }
    802        else
    803        {
    804            Log("Invalid save state size");
    805        }
    806    }
    807    else
    808    {
    809        Log("Invalid rom or memory rule");
    810    }
    811
    812    return false;
    813}
    814
    815void GearboyCore::SetCheat(const char* szCheat)
    816{
    817    std::string s = szCheat;
    818    if ((s.length() == 7) || (s.length() == 11))
    819    {
    820        m_pCartridge->SetGameGenieCheat(szCheat);
    821        if (m_pCartridge->IsLoadedROM())
    822            m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
    823    }
    824    else
    825    {
    826        m_pProcessor->SetGameSharkCheat(szCheat);
    827    }
    828}
    829
    830void GearboyCore::ClearCheats()
    831{
    832    m_pCartridge->ClearGameGenieCheats();
    833    m_pProcessor->ClearGameSharkCheats();
    834    if (m_pCartridge->IsLoadedROM())
    835        m_pMemory->LoadBank0and1FromROM(m_pCartridge->GetTheROM());
    836}
    837
    838void GearboyCore::SetRamModificationCallback(RamChangedCallback callback)
    839{
    840    m_pRamChangedCallback = callback;
    841}
    842
    843bool GearboyCore::IsCGB()
    844{
    845    return m_bCGB;
    846}
    847
    848bool GearboyCore::IsGBA()
    849{
    850    return m_bGBA;
    851}
    852
    853void GearboyCore::InitDMGPalette()
    854{
    855    GB_Color color[4];
    856
    857    color[0].red = 0x87;
    858    color[0].green = 0x96;
    859    color[0].blue = 0x03;
    860
    861    color[1].red = 0x4d;
    862    color[1].green = 0x6b;
    863    color[1].blue = 0x03;
    864
    865    color[2].red = 0x2b;
    866    color[2].green = 0x55;
    867    color[2].blue = 0x03;
    868
    869    color[3].red = 0x14;
    870    color[3].green = 0x44;
    871    color[3].blue = 0x03;
    872
    873    SetDMGPalette(color[0], color[1], color[2], color[3]);
    874}
    875
    876void GearboyCore::InitMemoryRules()
    877{
    878    m_pIORegistersMemoryRule = new IORegistersMemoryRule(m_pProcessor, m_pMemory, m_pVideo, m_pInput, m_pAudio);
    879
    880    m_pCommonMemoryRule = new CommonMemoryRule(m_pMemory);
    881
    882    m_pRomOnlyMemoryRule = new RomOnlyMemoryRule(m_pProcessor, m_pMemory,
    883            m_pVideo, m_pInput, m_pCartridge, m_pAudio);
    884
    885    m_pMBC1MemoryRule = new MBC1MemoryRule(m_pProcessor, m_pMemory,
    886            m_pVideo, m_pInput, m_pCartridge, m_pAudio);
    887
    888    m_pMultiMBC1MemoryRule = new MultiMBC1MemoryRule(m_pProcessor, m_pMemory,
    889            m_pVideo, m_pInput, m_pCartridge, m_pAudio);
    890
    891    m_pMBC2MemoryRule = new MBC2MemoryRule(m_pProcessor, m_pMemory,
    892            m_pVideo, m_pInput, m_pCartridge, m_pAudio);
    893
    894    m_pMBC3MemoryRule = new MBC3MemoryRule(m_pProcessor, m_pMemory,
    895            m_pVideo, m_pInput, m_pCartridge, m_pAudio);
    896
    897    m_pMBC5MemoryRule = new MBC5MemoryRule(m_pProcessor, m_pMemory,
    898            m_pVideo, m_pInput, m_pCartridge, m_pAudio);
    899
    900    m_pMemory->SetCurrentRule(m_pRomOnlyMemoryRule);
    901    m_pMemory->SetIORule(m_pIORegistersMemoryRule);
    902    m_pMemory->SetCommonRule(m_pCommonMemoryRule);
    903}
    904
    905bool GearboyCore::AddMemoryRules(Cartridge::CartridgeTypes forceType)
    906{
    907    m_pMemory->SetIORule(m_pIORegistersMemoryRule);
    908    m_pMemory->SetCommonRule(m_pCommonMemoryRule);
    909
    910    Cartridge::CartridgeTypes type = m_pCartridge->GetType();
    911
    912    bool notSupported = false;
    913
    914    if (forceType != Cartridge::CartridgeNotSupported)
    915        type = forceType;
    916
    917    switch (type)
    918    {
    919        case Cartridge::CartridgeNoMBC:
    920            m_pMemory->SetCurrentRule(m_pRomOnlyMemoryRule);
    921            break;
    922        case Cartridge::CartridgeMBC1:
    923            m_pMemory->SetCurrentRule(m_pMBC1MemoryRule);
    924            break;
    925        case Cartridge::CartridgeMBC1Multi:
    926            m_pMemory->SetCurrentRule(m_pMultiMBC1MemoryRule);
    927            break;
    928        case Cartridge::CartridgeMBC2:
    929            m_pMemory->SetCurrentRule(m_pMBC2MemoryRule);
    930            break;
    931        case Cartridge::CartridgeMBC3:
    932            m_pMemory->SetCurrentRule(m_pMBC3MemoryRule);
    933            break;
    934        case Cartridge::CartridgeMBC5:
    935            m_pMemory->SetCurrentRule(m_pMBC5MemoryRule);
    936            break;
    937        case Cartridge::CartridgeNotSupported:
    938            notSupported = true;
    939            break;
    940        default:
    941            notSupported = true;
    942    }
    943
    944    if (!notSupported)
    945    {
    946        m_pMemory->GetCurrentRule()->SetRamChangedCallback(m_pRamChangedCallback);
    947    }
    948
    949    return !notSupported;
    950}
    951
    952void GearboyCore::Reset(bool bCGB, bool bGBA)
    953{
    954    m_bCGB = bCGB;
    955    m_bGBA = bGBA;
    956
    957    if (m_bGBA && m_bCGB)
    958    {
    959        Log("Reset: Switching to Game Boy Advance");
    960    }
    961    else if (m_bCGB)
    962    {
    963        Log("Reset: Switching to Game Boy Color");
    964    }
    965    else
    966    {
    967        Log("Reset: Defaulting to Game Boy DMG");
    968    }
    969
    970    m_pMemory->Reset(m_bCGB);
    971    m_pProcessor->Reset(m_bCGB, m_bGBA);
    972    m_pVideo->Reset(m_bCGB);
    973    m_pAudio->Reset(m_bCGB);
    974    m_pInput->Reset();
    975    m_pCartridge->UpdateCurrentRTC();
    976    m_iRTCUpdateCount = 0;
    977
    978    m_pCommonMemoryRule->Reset(m_bCGB);
    979    m_pRomOnlyMemoryRule->Reset(m_bCGB);
    980    m_pMBC1MemoryRule->Reset(m_bCGB);
    981    m_pMultiMBC1MemoryRule->Reset(m_bCGB);
    982    m_pMBC2MemoryRule->Reset(m_bCGB);
    983    m_pMBC3MemoryRule->Reset(m_bCGB);
    984    m_pMBC5MemoryRule->Reset(m_bCGB);
    985    m_pIORegistersMemoryRule->Reset(m_bCGB);
    986
    987    m_bPaused = false;
    988}
    989
    990void GearboyCore::RenderDMGFrame(u16* pFrameBuffer) const
    991{
    992    if (IsValidPointer(pFrameBuffer))
    993    {
    994        int pixels = GAMEBOY_WIDTH * GAMEBOY_HEIGHT;
    995        const u8* pGameboyFrameBuffer = m_pVideo->GetFrameBuffer();
    996
    997        for (int i = 0; i < pixels; i++)
    998        {
    999            pFrameBuffer[i] = m_DMGPalette[pGameboyFrameBuffer[i]];
   1000        }
   1001    }
   1002}