cscg22-gearboy

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

Cartridge.cpp (15483B)


      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 <string>
     21#include <algorithm>
     22#include <ctype.h>
     23#include <time.h>
     24#include "Cartridge.h"
     25#include "miniz/miniz.c"
     26
     27Cartridge::Cartridge()
     28{
     29    InitPointer(m_pTheROM);
     30    m_iTotalSize = 0;
     31    m_szName[0] = 0;
     32    m_iROMSize = 0;
     33    m_iRAMSize = 0;
     34    m_Type = CartridgeNotSupported;
     35    m_bValidROM = false;
     36    m_bCGB = false;
     37    m_bSGB = false;
     38    m_iVersion = 0;
     39    m_bLoaded = false;
     40    m_RTCCurrentTime = 0;
     41    m_bBattery = false;
     42    m_szFilePath[0] = 0;
     43    m_szFileName[0] = 0;
     44    m_bRTCPresent = false;
     45    m_bRumblePresent = false;
     46    m_iRAMBankCount = 0;
     47    m_iROMBankCount = 0;
     48}
     49
     50Cartridge::~Cartridge()
     51{
     52    SafeDeleteArray(m_pTheROM);
     53}
     54
     55void Cartridge::Init()
     56{
     57    Reset();
     58}
     59
     60void Cartridge::Reset()
     61{
     62    SafeDeleteArray(m_pTheROM);
     63    m_iTotalSize = 0;
     64    m_szName[0] = 0;
     65    m_iROMSize = 0;
     66    m_iRAMSize = 0;
     67    m_Type = CartridgeNotSupported;
     68    m_bValidROM = false;
     69    m_bCGB = false;
     70    m_bSGB = false;
     71    m_iVersion = 0;
     72    m_bLoaded = false;
     73    m_RTCCurrentTime = 0;
     74    m_bBattery = false;
     75    m_szFilePath[0] = 0;
     76    m_szFileName[0] = 0;
     77    m_bRTCPresent = false;
     78    m_bRumblePresent = false;
     79    m_iRAMBankCount = 0;
     80    m_iROMBankCount = 0;
     81    m_GameGenieList.clear();
     82}
     83
     84bool Cartridge::IsValidROM() const
     85{
     86    return m_bValidROM;
     87}
     88
     89bool Cartridge::IsLoadedROM() const
     90{
     91    return m_bLoaded;
     92}
     93
     94Cartridge::CartridgeTypes Cartridge::GetType() const
     95{
     96    return m_Type;
     97}
     98
     99int Cartridge::GetRAMSize() const
    100{
    101    return m_iRAMSize;
    102}
    103
    104int Cartridge::GetROMSize() const
    105{
    106    return m_iROMSize;
    107}
    108
    109int Cartridge::GetRAMBankCount() const
    110{
    111    return m_iRAMBankCount;
    112}
    113
    114int Cartridge::GetROMBankCount() const
    115{
    116    return m_iROMBankCount;
    117}
    118
    119const char* Cartridge::GetName() const
    120{
    121    return m_szName;
    122}
    123
    124const char* Cartridge::GetFilePath() const
    125{
    126    return m_szFilePath;
    127}
    128
    129const char* Cartridge::GetFileName() const
    130{
    131    return m_szFileName;
    132}
    133
    134int Cartridge::GetTotalSize() const
    135{
    136    return m_iTotalSize;
    137}
    138
    139bool Cartridge::HasBattery() const
    140{
    141    return m_bBattery;
    142}
    143
    144u8* Cartridge::GetTheROM() const
    145{
    146    return m_pTheROM;
    147}
    148
    149bool Cartridge::LoadFromZipFile(const u8* buffer, int size)
    150{
    151    using namespace std;
    152
    153    mz_zip_archive zip_archive;
    154    mz_bool status;
    155    memset(&zip_archive, 0, sizeof (zip_archive));
    156
    157    status = mz_zip_reader_init_mem(&zip_archive, (void*) buffer, size, 0);
    158    if (!status)
    159    {
    160        Log("mz_zip_reader_init_mem() failed!");
    161        return false;
    162    }
    163
    164    for (unsigned int i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++)
    165    {
    166        mz_zip_archive_file_stat file_stat;
    167        if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat))
    168        {
    169            Log("mz_zip_reader_file_stat() failed!");
    170            mz_zip_reader_end(&zip_archive);
    171            return false;
    172        }
    173
    174        Log("ZIP Content - Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u", file_stat.m_filename, file_stat.m_comment, (unsigned int) file_stat.m_uncomp_size, (unsigned int) file_stat.m_comp_size);
    175
    176        string fn((const char*) file_stat.m_filename);
    177        transform(fn.begin(), fn.end(), fn.begin(), (int(*)(int)) tolower);
    178        string extension = fn.substr(fn.find_last_of(".") + 1);
    179
    180        if ((extension == "gb") || (extension == "dmg") || (extension == "gbc") || (extension == "cgb") || (extension == "sgb"))
    181        {
    182            void *p;
    183            size_t uncomp_size;
    184
    185            p = mz_zip_reader_extract_file_to_heap(&zip_archive, file_stat.m_filename, &uncomp_size, 0);
    186            if (!p)
    187            {
    188                Log("mz_zip_reader_extract_file_to_heap() failed!");
    189                mz_zip_reader_end(&zip_archive);
    190                return false;
    191            }
    192
    193            bool ok = LoadFromBuffer((const u8*) p, static_cast<int>(uncomp_size));
    194
    195            free(p);
    196            mz_zip_reader_end(&zip_archive);
    197
    198            return ok;
    199        }
    200    }
    201    return false;
    202}
    203
    204bool Cartridge::LoadFromFile(const char* path)
    205{
    206    using namespace std;
    207
    208    Log("Loading %s...", path);
    209
    210    Reset();
    211
    212    strcpy(m_szFilePath, path);
    213
    214    std::string pathstr(path);
    215    std::string filename;
    216
    217    size_t pos = pathstr.find_last_of("\\");
    218    if (pos != std::string::npos)
    219    {
    220        filename.assign(pathstr.begin() + pos + 1, pathstr.end());
    221    }
    222    else
    223    {
    224        pos = pathstr.find_last_of("/");
    225        if (pos != std::string::npos)
    226        {
    227            filename.assign(pathstr.begin() + pos + 1, pathstr.end());
    228        }
    229        else
    230        {
    231            filename = pathstr;
    232        }
    233    }
    234
    235    strcpy(m_szFileName, filename.c_str());
    236
    237    ifstream file(path, ios::in | ios::binary | ios::ate);
    238
    239    if (file.is_open())
    240    {
    241        int size = static_cast<int> (file.tellg());
    242        char* memblock = new char[size];
    243        file.seekg(0, ios::beg);
    244        file.read(memblock, size);
    245        file.close();
    246
    247        string fn(path);
    248        transform(fn.begin(), fn.end(), fn.begin(), (int(*)(int)) tolower);
    249        string extension = fn.substr(fn.find_last_of(".") + 1);
    250
    251        if (extension == "zip")
    252        {
    253            Log("Loading from ZIP...");
    254            m_bLoaded = LoadFromZipFile(reinterpret_cast<u8*> (memblock), size);
    255        }
    256        else
    257        {
    258            m_bLoaded = LoadFromBuffer(reinterpret_cast<u8*> (memblock), size);
    259        }
    260
    261        if (m_bLoaded)
    262        {
    263            Log("ROM loaded", path);
    264        }
    265        else
    266        {
    267            Log("There was a problem loading the memory for file %s...", path);
    268        }
    269
    270        SafeDeleteArray(memblock);
    271    }
    272    else
    273    {
    274        Log("There was a problem loading the file %s...", path);
    275        m_bLoaded = false;
    276    }
    277
    278    if (!m_bLoaded)
    279    {
    280        Reset();
    281    }
    282
    283    return m_bLoaded;
    284}
    285
    286bool Cartridge::LoadFromBuffer(const u8* buffer, int size)
    287{
    288    if (IsValidPointer(buffer))
    289    {
    290        Log("Loading from buffer... Size: %d", size);
    291        m_iTotalSize = size;
    292        m_pTheROM = new u8[m_iTotalSize];
    293        memcpy(m_pTheROM, buffer, m_iTotalSize);
    294        m_bLoaded = true;
    295        return GatherMetadata();
    296    }
    297    else
    298        return false;
    299}
    300
    301void Cartridge::CheckCartridgeType(int type)
    302{
    303    if ((type != 0xEA) && (GetROMSize() == 0))
    304        type = 0;
    305
    306    switch (type)
    307    {
    308        case 0x00:
    309            // NO MBC
    310        case 0x08:
    311            // ROM
    312            // SRAM
    313        case 0x09:
    314            // ROM
    315            // SRAM
    316            // BATT
    317            m_Type = CartridgeNoMBC;
    318            break;
    319        case 0x01:
    320            // MBC1
    321        case 0x02:
    322            // MBC1
    323            // SRAM
    324        case 0x03:
    325            // MBC1
    326            // SRAM
    327            // BATT
    328        case 0xEA:
    329            // Hack to accept 0xEA as a MBC1 (Sonic 3D Blast 5)
    330        case 0xFF:
    331            // Hack to accept HuC1 as a MBC1
    332            m_Type = CartridgeMBC1;
    333            break;
    334        case 0x05:
    335            // MBC2
    336            // SRAM
    337        case 0x06:
    338            // MBC2
    339            // SRAM
    340            // BATT
    341            m_Type = CartridgeMBC2;
    342            break;
    343        case 0x0F:
    344            // MBC3
    345            // TIMER
    346            // BATT
    347        case 0x10:
    348            // MBC3
    349            // TIMER
    350            // BATT
    351            // SRAM
    352        case 0x11:
    353            // MBC3
    354        case 0x12:
    355            // MBC3
    356            // SRAM
    357        case 0x13:
    358            // MBC3
    359            // BATT
    360            // SRAM
    361        case 0xFC:
    362            // Game Boy Camera
    363            m_Type = CartridgeMBC3;
    364            break;
    365        case 0x19:
    366            // MBC5
    367        case 0x1A:
    368            // MBC5
    369            // SRAM
    370        case 0x1B:
    371            // MBC5
    372            // BATT
    373            // SRAM
    374        case 0x1C:
    375            // RUMBLE
    376        case 0x1D:
    377            // RUMBLE
    378            // SRAM
    379        case 0x1E:
    380            // RUMBLE
    381            // BATT
    382            // SRAM
    383            m_Type = CartridgeMBC5;
    384            break;
    385        case 0x0B:
    386            // MMMO1
    387        case 0x0C:
    388            // MMM01
    389            // SRAM
    390        case 0x0D:
    391            // MMM01
    392            // SRAM
    393            // BATT
    394        case 0x15:
    395            // MBC4
    396        case 0x16:
    397            // MBC4
    398            // SRAM
    399        case 0x17:
    400            // MBC4
    401            // SRAM
    402            // BATT
    403        case 0x22:
    404            // MBC7
    405            // BATT
    406            // SRAM
    407        case 0x55:
    408            // GG
    409        case 0x56:
    410            // GS3
    411        case 0xFD:
    412            // TAMA 5
    413        case 0xFE:
    414            // HuC3
    415            m_Type = CartridgeNotSupported;
    416            Log("--> ** This cartridge is not supported. Type: %d", type);
    417            break;
    418        default:
    419            m_Type = CartridgeNotSupported;
    420            Log("--> ** Unknown cartridge type: %d", type);
    421    }
    422
    423    switch (type)
    424    {
    425        case 0x03:
    426        case 0x06:
    427        case 0x09:
    428        case 0x0D:
    429        case 0x0F:
    430        case 0x10:
    431        case 0x13:
    432        case 0x17:
    433        case 0x1B:
    434        case 0x1E:
    435        case 0x22:
    436        case 0xFD:
    437        case 0xFF:
    438            m_bBattery = true;
    439            break;
    440        default:
    441            m_bBattery = false;
    442    }
    443
    444    switch (type)
    445    {
    446        case 0x0F:
    447        case 0x10:
    448            m_bRTCPresent = true;
    449            break;
    450        default:
    451            m_bRTCPresent = false;
    452    }
    453
    454    switch (type)
    455    {
    456        case 0x1C:
    457        case 0x1D:
    458        case 0x1E:
    459            m_bRumblePresent = true;
    460            break;
    461        default:
    462            m_bRumblePresent = false;
    463    }
    464}
    465
    466int Cartridge::GetVersion() const
    467{
    468    return m_iVersion;
    469}
    470
    471bool Cartridge::IsSGB() const
    472{
    473    return m_bSGB;
    474}
    475
    476bool Cartridge::IsCGB() const
    477{
    478    return m_bCGB;
    479}
    480
    481void Cartridge::UpdateCurrentRTC()
    482{
    483    time(&m_RTCCurrentTime);
    484}
    485
    486time_t Cartridge::GetCurrentRTC()
    487{
    488    return m_RTCCurrentTime;
    489}
    490
    491bool Cartridge::IsRTCPresent() const
    492{
    493    return m_bRTCPresent;
    494}
    495
    496bool Cartridge::IsRumblePresent() const
    497{
    498    return m_bRumblePresent;
    499}
    500
    501void Cartridge::SetGameGenieCheat(const char* szCheat)
    502{
    503    std::string code(szCheat);
    504    for (std::string::iterator p = code.begin(); code.end() != p; ++p)
    505        *p = toupper(*p);
    506
    507    if (m_bLoaded && (code.length() > 6) && ((code[3] < '0') || ((code[3] > '9') && (code[3] < 'A'))))
    508    {
    509        u8 new_value = (AsHex(code[0]) << 4 | AsHex(code[1])) & 0xFF;
    510        u16 cheat_address = (AsHex(code[2]) << 8 | AsHex(code[4]) << 4 | AsHex(code[5]) | (AsHex(code[6]) ^ 0xF) << 12) & 0x7FFF;
    511        bool avoid_compare = true;
    512        u8 compare_value = 0;
    513
    514        if ((code.length() == 11) && ((code[7] < '0') || ((code[7] > '9') && (code[7] < 'A'))))
    515        {
    516            compare_value = (AsHex(code[8]) << 4 | AsHex(code[10])) ^ 0xFF;
    517            compare_value = ((compare_value >> 2 | compare_value << 6) ^ 0x45) & 0xFF;
    518            avoid_compare = false;
    519        }
    520
    521        for (int bank = 0; bank < GetROMBankCount(); bank++)
    522        {
    523            int bank_address = (bank * 0x4000) + (cheat_address & 0x3FFF);
    524
    525            if (avoid_compare || (m_pTheROM[bank_address] == compare_value))
    526            {
    527                GameGenieCode undo_data;
    528                undo_data.address = bank_address;
    529                undo_data.old_value = m_pTheROM[bank_address];
    530
    531                m_pTheROM[bank_address] = new_value;
    532
    533                m_GameGenieList.push_back(undo_data);
    534            }
    535        }
    536    }
    537}
    538
    539void Cartridge::ClearGameGenieCheats()
    540{
    541    std::list<GameGenieCode>::iterator it;
    542
    543    for (it = m_GameGenieList.begin(); it != m_GameGenieList.end(); it++)
    544    {
    545        m_pTheROM[it->address] = it->old_value;
    546    }
    547
    548    m_GameGenieList.clear();
    549}
    550
    551unsigned int Cartridge::Pow2Ceil(unsigned int n)
    552{
    553    --n;
    554    n |= n >> 1;
    555    n |= n >> 2;
    556    n |= n >> 4;
    557    n |= n >> 8;
    558    ++n;
    559    return n;
    560}
    561
    562bool Cartridge::GatherMetadata()
    563{
    564    char name[12] = {0};
    565    name[11] = 0;
    566
    567    for (int i = 0; i < 11; i++)
    568    {
    569        name[i] = m_pTheROM[0x0134 + i];
    570
    571        if (name[i] == 0)
    572        {
    573            break;
    574        }
    575    }
    576
    577    strcpy(m_szName, name);
    578
    579    m_bCGB = (m_pTheROM[0x143] == 0x80) || (m_pTheROM[0x143] == 0xC0);
    580    m_bSGB = (m_pTheROM[0x146] == 0x03);
    581    int type = m_pTheROM[0x147];
    582    m_iROMSize = m_pTheROM[0x148];
    583    m_iRAMSize = m_pTheROM[0x149];
    584    m_iVersion = m_pTheROM[0x14C];
    585
    586    CheckCartridgeType(type);
    587
    588    switch (m_iRAMSize)
    589    {
    590        case 0x00:
    591            m_iRAMBankCount = (m_Type == Cartridge::CartridgeMBC2) ? 1 : 0;
    592            break;
    593        case 0x01:
    594        case 0x02:
    595            m_iRAMBankCount = 1;
    596            break;
    597        case 0x04:
    598            m_iRAMBankCount = 16;
    599            break;
    600        default:
    601            m_iRAMBankCount = 4;
    602            break;
    603    }
    604
    605    m_iROMBankCount = std::max(Pow2Ceil(m_iTotalSize / 0x4000), 2u);
    606
    607    bool presumeMultiMBC1 = ((type == 1) && (m_iRAMSize == 0) && (m_iROMBankCount == 64));
    608
    609    if ((m_Type == Cartridge::CartridgeMBC1) && presumeMultiMBC1)
    610    {
    611        m_Type = Cartridge::CartridgeMBC1Multi;
    612        Log("Presumed Multi 64");
    613    }
    614
    615    Log("Cartridge Size %d", m_iTotalSize);
    616    Log("ROM Name %s", m_szName);
    617    Log("ROM Version %d", m_iVersion);
    618    Log("ROM Type %X", type);
    619    Log("ROM Size %X", m_iROMSize);
    620    Log("ROM Bank Count %d", m_iROMBankCount);
    621    Log("RAM Size %X", m_iRAMSize);
    622    Log("RAM Bank Count %d", m_iRAMBankCount);
    623
    624    switch (m_Type)
    625    {
    626        case Cartridge::CartridgeNoMBC:
    627            Log("No MBC found");
    628            break;
    629        case Cartridge::CartridgeMBC1:
    630            Log("MBC1 found");
    631            break;
    632        case Cartridge::CartridgeMBC1Multi:
    633            Log("MBC1 Multi 64 found");
    634            break;
    635        case Cartridge::CartridgeMBC2:
    636            Log("MBC2 found");
    637            break;
    638        case Cartridge::CartridgeMBC3:
    639            Log("MBC3 found");
    640            break;
    641        case Cartridge::CartridgeMBC5:
    642            Log("MBC5 found");
    643            break;
    644        case Cartridge::CartridgeNotSupported:
    645            Log("Cartridge not supported!!");
    646            break;
    647        default:
    648            break;
    649    }
    650
    651    if (m_bBattery)
    652    {
    653        Log("Battery powered RAM found");
    654    }
    655
    656    if (m_pTheROM[0x143] == 0xC0)
    657    {
    658        Log("Game Boy Color only");
    659    }
    660    else if (m_bCGB)
    661    {
    662        Log("Game Boy Color supported");
    663    }
    664
    665    if (m_bSGB)
    666    {
    667        Log("Super Game Boy supported");
    668    }
    669
    670    int checksum = 0;
    671
    672    for (int j = 0x134; j < 0x14E; j++)
    673    {
    674        checksum += m_pTheROM[j];
    675    }
    676
    677    m_bValidROM = ((checksum + 25) & 0xFF) == 0;
    678
    679    if (m_bValidROM)
    680    {
    681        Log("Checksum OK!");
    682    }
    683    else
    684    {
    685        Log("Checksum FAILED!!!");
    686    }
    687
    688    return (m_Type != CartridgeNotSupported);
    689}