Move GB/GBC header parsing to gbCartData
This adds a new class, gbCartData to do all of the GB/GBC header parsing in a single location. Breaking change: ROMs advertising a 2 KiB RAM size now properly only have 2 KiB allocated for RAM, whereas it used to be 8 KiB. This was tested with the one aftermarket ROM making use of this RAM size (Quartet), with no issue. Save files and save state files should still be compatible since the RAM was saved with the correct 2 KiB size.
This commit is contained in:
parent
e505236ec4
commit
0132c76100
|
@ -882,6 +882,7 @@ set(
|
|||
src/common/iniparser.h
|
||||
src/common/memgzio.h
|
||||
src/common/Port.h
|
||||
src/common/sizes.h
|
||||
src/common/SoundDriver.h
|
||||
src/common/SoundSDL.h
|
||||
)
|
||||
|
@ -966,6 +967,7 @@ set(
|
|||
set(
|
||||
SRC_GB
|
||||
src/gb/GB.cpp
|
||||
src/gb/gbCartData.cpp
|
||||
src/gb/gbCheats.cpp
|
||||
src/gb/gbDis.cpp
|
||||
src/gb/gbGfx.cpp
|
||||
|
@ -979,6 +981,7 @@ set(
|
|||
set(
|
||||
HDR_GB
|
||||
src/gb/gb.h
|
||||
src/gb/gbCartData.h
|
||||
src/gb/gbCheats.h
|
||||
src/gb/gbCodes.h
|
||||
src/gb/gbCodesCB.h
|
||||
|
|
|
@ -43,3 +43,5 @@
|
|||
#define MSG_OUT_OF_MEMORY 41
|
||||
#define MSG_WRONG_GAMESHARK_CODE 42
|
||||
#define MSG_UNSUPPORTED_GAMESHARK_CODE 43
|
||||
#define MSG_INVALID_GAME_BOY_NINTENDO_LOGO 44
|
||||
#define MSG_INVALID_HEADER_CHECKSUM 45
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef VBAM_COMMON_SIZES_H_
|
||||
#define VBAM_COMMON_SIZES_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// Various size constants.
|
||||
constexpr size_t k256B = 256;
|
||||
constexpr size_t k512B = 2 * k256B;
|
||||
constexpr size_t k1KiB = 2 * k512B;
|
||||
constexpr size_t k2KiB = 2 * k1KiB;
|
||||
constexpr size_t k4KiB = 2 * k2KiB;
|
||||
constexpr size_t k8KiB = 2 * k4KiB;
|
||||
constexpr size_t k16KiB = 2 * k8KiB;
|
||||
constexpr size_t k32KiB = 2 * k16KiB;
|
||||
constexpr size_t k64KiB = 2 * k32KiB;
|
||||
constexpr size_t k128KiB = 2 * k64KiB;
|
||||
constexpr size_t k256KiB = 2 * k128KiB;
|
||||
constexpr size_t k512KiB = 2 * k256KiB;
|
||||
constexpr size_t k1MiB = 2 * k512KiB;
|
||||
constexpr size_t k2MiB = 2 * k1MiB;
|
||||
constexpr size_t k4MiB = 2 * k2MiB;
|
||||
constexpr size_t k8MiB = 2 * k4MiB;
|
||||
|
||||
#endif // VBAM_COMMON_SIZES_H_
|
702
src/gb/GB.cpp
702
src/gb/GB.cpp
|
@ -10,6 +10,7 @@
|
|||
#include "../NLS.h"
|
||||
#include "../System.h"
|
||||
#include "../Util.h"
|
||||
#include "../common/sizes.h"
|
||||
#include "../gba/GBALink.h"
|
||||
#include "../gba/Sound.h"
|
||||
#include "gbCheats.h"
|
||||
|
@ -18,12 +19,24 @@
|
|||
#include "gbSGB.h"
|
||||
#include "gbSound.h"
|
||||
|
||||
#ifndef __LIBRETRO__
|
||||
#include "../common/Patch.h"
|
||||
#endif // __LIBRETRO__
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define _stricmp strcasecmp
|
||||
#endif
|
||||
|
||||
extern uint8_t* pix;
|
||||
|
||||
namespace {
|
||||
|
||||
// Mapper functions.
|
||||
void (*g_mapper)(uint16_t, uint8_t) = nullptr;
|
||||
void (*g_mapperRAM)(uint16_t, uint8_t) = nullptr;
|
||||
uint8_t (*g_mapperReadRAM)(uint16_t) = nullptr;
|
||||
void (*g_mapperUpdateClock)() = nullptr;
|
||||
|
||||
// These are the default palettes when launching a GB game for GBC, GBA and
|
||||
// GBA SP, respectively.
|
||||
static constexpr std::array<uint16_t, 0x40> kGbGbcPalette = {
|
||||
|
@ -59,10 +72,209 @@ static constexpr std::array<uint16_t, 0x40> kGbGbaSpPalette = {
|
|||
0xa727, 0x6266, 0xe27b, 0xe3fc, 0x1f76, 0xf158, 0x468e, 0xa540,
|
||||
};
|
||||
|
||||
static constexpr size_t kTama5RamSize = k256B;
|
||||
|
||||
int gbGetValue(int min, int max, int v) {
|
||||
return (int)(min + (float)(max - min) *
|
||||
(2.0 * (v / 31.0) - (v / 31.0) * (v / 31.0)));
|
||||
}
|
||||
|
||||
void gbGenFilter() {
|
||||
for (int r = 0; r < 32; r++) {
|
||||
for (int g = 0; g < 32; g++) {
|
||||
for (int b = 0; b < 32; b++) {
|
||||
int nr =
|
||||
gbGetValue(gbGetValue(4, 14, g), gbGetValue(24, 29, g), r) -
|
||||
4;
|
||||
int ng = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, r),
|
||||
14 + gbGetValue(0, 3, r), b),
|
||||
gbGetValue(24 + gbGetValue(0, 3, r),
|
||||
29 + gbGetValue(0, 1, r), b),
|
||||
g) -
|
||||
4;
|
||||
int nb = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, r),
|
||||
14 + gbGetValue(0, 3, r), g),
|
||||
gbGetValue(24 + gbGetValue(0, 3, r),
|
||||
29 + gbGetValue(0, 1, r), g),
|
||||
b) -
|
||||
4;
|
||||
gbColorFilter[(b << 10) | (g << 5) | r] =
|
||||
(nb << 10) | (ng << 5) | nr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the `g_gbCartData` variable with the data in `gbRom`, expecting a
|
||||
// size of `romSize` bytes. Returns true on success.
|
||||
bool gbInitializeRom(size_t romSize) {
|
||||
g_gbCartData = gbCartData(gbRom, romSize);
|
||||
|
||||
if (!g_gbCartData) {
|
||||
switch (g_gbCartData.validity()) {
|
||||
case gbCartData::Validity::kValid:
|
||||
case gbCartData::Validity::kUninitialized:
|
||||
assert(false);
|
||||
break;
|
||||
case gbCartData::Validity::kSizeTooSmall:
|
||||
systemMessage(MSG_UNSUPPORTED_ROM_SIZE,
|
||||
N_("Unsupported rom size %02x"), romSize);
|
||||
break;
|
||||
case gbCartData::Validity::kUnknownMapperType:
|
||||
systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE,
|
||||
N_("Unknown cartridge type %02x"),
|
||||
g_gbCartData.mapper_flag());
|
||||
break;
|
||||
case gbCartData::Validity::kUnknownRomSize:
|
||||
systemMessage(MSG_UNSUPPORTED_ROM_SIZE,
|
||||
N_("Unsupported rom size %02x"),
|
||||
g_gbCartData.rom_flag());
|
||||
break;
|
||||
case gbCartData::Validity::kUnknownRamSize:
|
||||
systemMessage(MSG_UNSUPPORTED_RAM_SIZE,
|
||||
N_("Unsupported ram size %02x"),
|
||||
g_gbCartData.ram_flag());
|
||||
break;
|
||||
case gbCartData::Validity::kNoNintendoLogo:
|
||||
systemMessage(MSG_INVALID_GAME_BOY_NINTENDO_LOGO,
|
||||
N_("No Nintendo logo in header"));
|
||||
break;
|
||||
case gbCartData::Validity::kInvalidHeaderChecksum:
|
||||
systemMessage(
|
||||
MSG_INVALID_HEADER_CHECKSUM,
|
||||
N_("Invalid header checksum. Found: %02x. Expected: %02x"),
|
||||
g_gbCartData.header_checksum(),
|
||||
g_gbCartData.actual_header_checksum());
|
||||
break;
|
||||
}
|
||||
g_gbCartData = gbCartData();
|
||||
return false;
|
||||
}
|
||||
|
||||
// We ignore romSize > romHeaderSize here, for backwards compatibility. This
|
||||
// is necessary for some ROM hacks.
|
||||
const size_t romHeaderSize = g_gbCartData.rom_size();
|
||||
if (romSize < romHeaderSize) {
|
||||
uint8_t* gbRomNew = (uint8_t*)realloc(gbRom, romHeaderSize);
|
||||
if (!gbRomNew) {
|
||||
assert(false);
|
||||
return false;
|
||||
};
|
||||
gbRom = gbRomNew;
|
||||
|
||||
// Not sure if it's 0x00, 0xff or random data.
|
||||
std::fill(gbRom + romSize, gbRom + romHeaderSize, (uint8_t)0);
|
||||
}
|
||||
|
||||
// Set up globals for compatibility.
|
||||
gbRomType = g_gbCartData.mapper_flag();
|
||||
gbRom[0x147] = g_gbCartData.mapper_flag();
|
||||
gbRomSize = g_gbCartData.rom_size();
|
||||
gbRomSizeMask = g_gbCartData.rom_mask();
|
||||
gbRamSize = g_gbCartData.ram_size();
|
||||
gbRamSizeMask = g_gbCartData.ram_mask();
|
||||
gbBattery = g_gbCartData.has_battery();
|
||||
gbRTCPresent = g_gbCartData.has_rtc();
|
||||
|
||||
// The initial RAM byte value.
|
||||
uint8_t gbRamFill = 0xff;
|
||||
|
||||
switch (g_gbCartData.mapper_type()) {
|
||||
case gbCartData::MapperType::kNone:
|
||||
case gbCartData::MapperType::kMbc1:
|
||||
g_mapper = mapperMBC1ROM;
|
||||
g_mapperRAM = mapperMBC1RAM;
|
||||
g_mapperReadRAM = mapperMBC1ReadRAM;
|
||||
break;
|
||||
case gbCartData::MapperType::kMbc2:
|
||||
g_mapper = mapperMBC2ROM;
|
||||
g_mapperRAM = mapperMBC2RAM;
|
||||
break;
|
||||
case gbCartData::MapperType::kMmm01:
|
||||
g_mapper = mapperMMM01ROM;
|
||||
g_mapperRAM = mapperMMM01RAM;
|
||||
break;
|
||||
case gbCartData::MapperType::kMbc3:
|
||||
case gbCartData::MapperType::kPocketCamera:
|
||||
g_mapper = mapperMBC3ROM;
|
||||
g_mapperRAM = mapperMBC3RAM;
|
||||
g_mapperReadRAM = mapperMBC3ReadRAM;
|
||||
break;
|
||||
case gbCartData::MapperType::kMbc5:
|
||||
g_mapper = mapperMBC5ROM;
|
||||
g_mapperRAM = mapperMBC5RAM;
|
||||
g_mapperReadRAM = mapperMBC5ReadRAM;
|
||||
break;
|
||||
case gbCartData::MapperType::kMbc7:
|
||||
g_mapper = mapperMBC7ROM;
|
||||
g_mapperRAM = mapperMBC7RAM;
|
||||
g_mapperReadRAM = mapperMBC7ReadRAM;
|
||||
break;
|
||||
case gbCartData::MapperType::kGameGenie:
|
||||
// Clean-up Game Genie hardware registers.
|
||||
for (int i = 0; i < 0x20; i++) {
|
||||
gbRom[0x4000 + i] = 0;
|
||||
}
|
||||
g_mapper = mapperGGROM;
|
||||
break;
|
||||
case gbCartData::MapperType::kGameShark:
|
||||
g_mapper = mapperGS3ROM;
|
||||
break;
|
||||
case gbCartData::MapperType::kTama5:
|
||||
if (gbRam != nullptr) {
|
||||
free(gbRam);
|
||||
gbRam = nullptr;
|
||||
}
|
||||
|
||||
gbRamFill = 0x00;
|
||||
if (gbTAMA5ram == nullptr)
|
||||
gbTAMA5ram = (uint8_t*)malloc(kTama5RamSize);
|
||||
memset(gbTAMA5ram, 0x0, kTama5RamSize);
|
||||
|
||||
g_mapperRAM = mapperTAMA5RAM;
|
||||
g_mapperReadRAM = mapperTAMA5ReadRAM;
|
||||
g_mapperUpdateClock = memoryUpdateTAMA5Clock;
|
||||
break;
|
||||
case gbCartData::MapperType::kHuC3:
|
||||
g_mapper = mapperHuC3ROM;
|
||||
g_mapperRAM = mapperHuC3RAM;
|
||||
g_mapperReadRAM = mapperHuC3ReadRAM;
|
||||
break;
|
||||
case gbCartData::MapperType::kHuC1:
|
||||
g_mapper = mapperHuC1ROM;
|
||||
g_mapperRAM = mapperHuC1RAM;
|
||||
break;
|
||||
default:
|
||||
systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE,
|
||||
N_("Unknown cartridge type"));
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t ramSize = g_gbCartData.ram_size();
|
||||
if (ramSize > 0) {
|
||||
gbRam = (uint8_t*)malloc(ramSize);
|
||||
memset(gbRam, gbRamFill, ramSize);
|
||||
}
|
||||
|
||||
gbGenFilter();
|
||||
gbSgbInit();
|
||||
setColorizerHack(false);
|
||||
|
||||
gbMemory = (uint8_t*)malloc(65536);
|
||||
|
||||
#ifdef __LIBRETRO__
|
||||
pix = (uint8_t*)calloc(1, 4 * 256 * 224);
|
||||
#else
|
||||
pix = (uint8_t*)calloc(1, 4 * 257 * 226);
|
||||
#endif
|
||||
|
||||
gbLineBuffer = (uint16_t*)malloc(160 * sizeof(uint16_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern uint8_t* pix;
|
||||
bool gbUpdateSizes();
|
||||
bool inBios = false;
|
||||
|
||||
// debugging
|
||||
|
@ -71,12 +283,6 @@ char gbBuffer[2048];
|
|||
|
||||
extern uint16_t gbLineMix[160];
|
||||
|
||||
// mappers
|
||||
void (*mapper)(uint16_t, uint8_t) = NULL;
|
||||
void (*mapperRAM)(uint16_t, uint8_t) = NULL;
|
||||
uint8_t (*mapperReadRAM)(uint16_t) = NULL;
|
||||
void (*mapperUpdateClock)() = NULL;
|
||||
|
||||
// registers
|
||||
gbRegister PC;
|
||||
gbRegister SP;
|
||||
|
@ -147,6 +353,7 @@ int GBSYNCHRONIZE_CLOCK_TICKS = 52920;
|
|||
// state variables
|
||||
|
||||
// general
|
||||
gbCartData g_gbCartData;
|
||||
int clockTicks = 0;
|
||||
bool gbSystemMessage = false;
|
||||
int gbGBCColorType = 0;
|
||||
|
@ -233,7 +440,6 @@ uint32_t gbTimeNow = 0;
|
|||
int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS;
|
||||
// emulator features
|
||||
int gbBattery = 0;
|
||||
int gbRumble = 0;
|
||||
int gbRTCPresent = 0;
|
||||
bool gbBatteryError = false;
|
||||
int gbCaptureNumber = 0;
|
||||
|
@ -242,45 +448,6 @@ bool gbCapturePrevious = false;
|
|||
int gbJoymask[4] = { 0, 0, 0, 0 };
|
||||
static bool allow_colorizer_hack;
|
||||
|
||||
uint8_t gbRamFill = 0xff;
|
||||
|
||||
int gbRomSizes[] = {
|
||||
0x00008000, // 32K
|
||||
0x00010000, // 64K
|
||||
0x00020000, // 128K
|
||||
0x00040000, // 256K
|
||||
0x00080000, // 512K
|
||||
0x00100000, // 1024K
|
||||
0x00200000, // 2048K
|
||||
0x00400000, // 4096K
|
||||
0x00800000 // 8192K
|
||||
};
|
||||
int gbRomSizesMasks[] = { 0x00007fff,
|
||||
0x0000ffff,
|
||||
0x0001ffff,
|
||||
0x0003ffff,
|
||||
0x0007ffff,
|
||||
0x000fffff,
|
||||
0x001fffff,
|
||||
0x003fffff,
|
||||
0x007fffff };
|
||||
|
||||
int gbRamSizes[6] = {
|
||||
0x00000000, // 0K
|
||||
0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap...
|
||||
0x00002000, // 8K
|
||||
0x00008000, // 32K
|
||||
0x00020000, // 128K
|
||||
0x00010000 // 64K
|
||||
};
|
||||
|
||||
int gbRamSizesMasks[6] = { 0x00000000,
|
||||
0x000007ff,
|
||||
0x00001fff,
|
||||
0x00007fff,
|
||||
0x0001ffff,
|
||||
0x0000ffff };
|
||||
|
||||
int gbCycles[] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0
|
||||
|
@ -817,34 +984,6 @@ static const uint16_t gbColorizationPaletteData[32][3][4] = {
|
|||
#define GBSAVE_GAME_VERSION_12 12
|
||||
#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_12
|
||||
|
||||
|
||||
static bool gbCheckRomHeader(void)
|
||||
{
|
||||
const uint8_t nlogo[16] = {
|
||||
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
|
||||
0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D
|
||||
};
|
||||
|
||||
// Game Genie
|
||||
if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) &&
|
||||
(gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) {
|
||||
return true;
|
||||
|
||||
// Game Shark
|
||||
} else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) ||
|
||||
((gbRom[0x165] == 0x3E) && (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F))) {
|
||||
return true;
|
||||
|
||||
// check for 1st 16 bytes of nintendo logo
|
||||
} else {
|
||||
uint8_t header[16];
|
||||
memcpy(header, &gbRom[0x104], 16);
|
||||
if (!memcmp(header, nlogo, 16))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setColorizerHack(bool value)
|
||||
{
|
||||
allow_colorizer_hack = value;
|
||||
|
@ -890,37 +1029,6 @@ static inline bool gbCgbPaletteAccessValid(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
int inline gbGetValue(int min, int max, int v)
|
||||
{
|
||||
return (int)(min + (float)(max - min) * (2.0 * (v / 31.0) - (v / 31.0) * (v / 31.0)));
|
||||
}
|
||||
|
||||
void gbGenFilter()
|
||||
{
|
||||
for (int r = 0; r < 32; r++) {
|
||||
for (int g = 0; g < 32; g++) {
|
||||
for (int b = 0; b < 32; b++) {
|
||||
int nr = gbGetValue(gbGetValue(4, 14, g),
|
||||
gbGetValue(24, 29, g), r)
|
||||
- 4;
|
||||
int ng = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, r),
|
||||
14 + gbGetValue(0, 3, r), b),
|
||||
gbGetValue(24 + gbGetValue(0, 3, r),
|
||||
29 + gbGetValue(0, 1, r), b),
|
||||
g)
|
||||
- 4;
|
||||
int nb = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, r),
|
||||
14 + gbGetValue(0, 3, r), g),
|
||||
gbGetValue(24 + gbGetValue(0, 3, r),
|
||||
29 + gbGetValue(0, 1, r), g),
|
||||
b)
|
||||
- 4;
|
||||
gbColorFilter[(b << 10) | (g << 5) | r] = (nb << 10) | (ng << 5) | nr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool gbIsGameboyRom(char* file)
|
||||
{
|
||||
if (strlen(file) > 4) {
|
||||
|
@ -1027,8 +1135,8 @@ void gbWriteMemory(uint16_t address, uint8_t value)
|
|||
}
|
||||
|
||||
#endif
|
||||
if (mapper)
|
||||
(*mapper)(address, value);
|
||||
if (g_mapper)
|
||||
(*g_mapper)(address, value);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1053,9 +1161,9 @@ void gbWriteMemory(uint16_t address, uint8_t value)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Is that a correct fix ??? (it used to be 'if (mapper)')...
|
||||
if (mapperRAM)
|
||||
(*mapperRAM)(address, value);
|
||||
// Is that a correct fix ??? (it used to be 'if (g_mapper)')...
|
||||
if (g_mapperRAM)
|
||||
(*g_mapperRAM)(address, value);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1866,8 +1974,9 @@ uint8_t gbReadMemory(uint16_t address)
|
|||
// but now its sram test fails, as the it expects 8kb and not 2kb...
|
||||
// So use the 'genericflashcard' option to fix it).
|
||||
if (address <= (0xa000 + gbRamSizeMask)) {
|
||||
if (mapperReadRAM)
|
||||
return mapperReadRAM(address);
|
||||
if (g_mapperReadRAM) {
|
||||
return g_mapperReadRAM(address);
|
||||
}
|
||||
return gbMemoryMap[address >> 12][address & 0x0fff];
|
||||
}
|
||||
return 0xff;
|
||||
|
@ -2263,21 +2372,28 @@ void gbCPUInit(const char* biosFileName, bool useBiosFile)
|
|||
|
||||
void gbGetHardwareType()
|
||||
{
|
||||
gbCgbMode = 0;
|
||||
gbSgbMode = 0;
|
||||
if ((gbEmulatorType == 0 && (gbRom[0x143] & 0x80)) || gbEmulatorType == 1 || gbEmulatorType == 4) {
|
||||
gbCgbMode = 1;
|
||||
if (g_gbCartData.header_type() == gbCartData::RomHeaderType::kInvalid) {
|
||||
gbHardware = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gbCgbMode == 0) && (gbRom[0x146] == 0x03)) {
|
||||
gbCgbMode = false;
|
||||
gbSgbMode = false;
|
||||
if ((gbEmulatorType == 0 && g_gbCartData.SupportsCGB()) ||
|
||||
gbEmulatorType == 1 || gbEmulatorType == 4) {
|
||||
gbCgbMode = true;
|
||||
}
|
||||
|
||||
if ((!gbCgbMode) && (g_gbCartData.sgb_support())) {
|
||||
if (gbEmulatorType == 0 || gbEmulatorType == 2 || gbEmulatorType == 5)
|
||||
gbSgbMode = 1;
|
||||
gbSgbMode = true;
|
||||
}
|
||||
|
||||
gbHardware = 1; // GB
|
||||
if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1))
|
||||
if ((gbCgbMode && (gbEmulatorType == 0)) || (gbEmulatorType == 1))
|
||||
gbHardware = 2; // GBC
|
||||
else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5))
|
||||
else if ((gbSgbMode && (gbEmulatorType == 0)) || (gbEmulatorType == 2) ||
|
||||
(gbEmulatorType == 5))
|
||||
gbHardware = 4; // SGB(2)
|
||||
else if (gbEmulatorType == 4)
|
||||
gbHardware = 8; // GBA
|
||||
|
@ -2678,7 +2794,7 @@ void gbReset()
|
|||
}
|
||||
|
||||
// The CGB BIOS palette selection has to be done by VBA if BIOS is skipped.
|
||||
if (!(gbRom[0x143] & 0x80) && !inBios) {
|
||||
if (!g_gbCartData.RequiresCGB() && !inBios) {
|
||||
gbSelectColorizationPalette();
|
||||
}
|
||||
}
|
||||
|
@ -2732,7 +2848,7 @@ void gbReset()
|
|||
|
||||
memset(&gbDataMBC5, 0, sizeof(gbDataMBC5));
|
||||
gbDataMBC5.mapperROMBank = 1;
|
||||
gbDataMBC5.isRumbleCartridge = gbRumble;
|
||||
gbDataMBC5.isRumbleCartridge = g_gbCartData.has_rumble();
|
||||
|
||||
memset(&gbDataHuC1, 0, sizeof(gbDataHuC1));
|
||||
gbDataHuC1.mapperROMBank = 1;
|
||||
|
@ -2925,10 +3041,7 @@ void gbWriteSaveTAMA5(const char* name, bool extendedSave)
|
|||
(gbRamSizeMask + 1),
|
||||
gzFile);
|
||||
|
||||
fwrite(gbTAMA5ram,
|
||||
1,
|
||||
(gbTAMA5ramSize),
|
||||
gzFile);
|
||||
fwrite(gbTAMA5ram, 1, (kTama5RamSize), gzFile);
|
||||
|
||||
if (extendedSave)
|
||||
fwrite(&gbDataTAMA5.mapperSeconds,
|
||||
|
@ -3248,13 +3361,12 @@ bool gbReadSaveTAMA5(const char* name)
|
|||
else
|
||||
read = gbRamSizeMask;
|
||||
|
||||
read += gzread(gzFile,
|
||||
gbTAMA5ram,
|
||||
gbTAMA5ramSize);
|
||||
read += gzread(gzFile, gbTAMA5ram, kTama5RamSize);
|
||||
|
||||
bool res = true;
|
||||
const int tama5RamSize = kTama5RamSize;
|
||||
|
||||
if (read != (gbRamSizeMask + gbTAMA5ramSize + 1)) {
|
||||
if (read != (gbRamSizeMask + tama5RamSize + 1)) {
|
||||
systemMessage(MSG_FAILED_TO_READ_SGM,
|
||||
N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read);
|
||||
gbBatteryError = true;
|
||||
|
@ -3755,7 +3867,7 @@ static bool gbWriteSaveState(gzFile gzFile)
|
|||
utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3));
|
||||
utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5));
|
||||
if (gbTAMA5ram != NULL)
|
||||
utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize);
|
||||
utilGzWrite(gzFile, gbTAMA5ram, kTama5RamSize);
|
||||
utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01));
|
||||
|
||||
utilGzWrite(gzFile, gbPalette, 128 * sizeof(uint16_t));
|
||||
|
@ -3911,9 +4023,9 @@ static bool gbReadSaveState(gzFile gzFile)
|
|||
utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5));
|
||||
if (gbTAMA5ram != NULL) {
|
||||
if (coreOptions.skipSaveGameBattery) {
|
||||
utilGzSeek(gzFile, gbTAMA5ramSize, SEEK_CUR);
|
||||
utilGzSeek(gzFile, kTama5RamSize, SEEK_CUR);
|
||||
} else {
|
||||
utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize);
|
||||
utilGzRead(gzFile, gbTAMA5ram, kTama5RamSize);
|
||||
}
|
||||
}
|
||||
utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01));
|
||||
|
@ -4259,293 +4371,36 @@ void gbCleanUp()
|
|||
gbTAMA5ram = NULL;
|
||||
}
|
||||
|
||||
g_gbCartData = gbCartData();
|
||||
g_mapper = nullptr;
|
||||
g_mapperRAM = nullptr;
|
||||
g_mapperReadRAM = nullptr;
|
||||
g_mapperUpdateClock = nullptr;
|
||||
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
||||
}
|
||||
|
||||
bool gbLoadRom(const char* szFile)
|
||||
{
|
||||
int size = 0;
|
||||
bool gbLoadRom(const char* filename) {
|
||||
int romSize = 0;
|
||||
|
||||
if (gbRom != NULL) {
|
||||
if (gbRom != nullptr) {
|
||||
gbCleanUp();
|
||||
}
|
||||
|
||||
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
||||
|
||||
gbRom = utilLoad(szFile,
|
||||
utilIsGBImage,
|
||||
NULL,
|
||||
size);
|
||||
gbRom = utilLoad(filename, utilIsGBImage, nullptr, romSize);
|
||||
if (!gbRom)
|
||||
return false;
|
||||
|
||||
gbRomSize = size;
|
||||
|
||||
gbBatteryError = false;
|
||||
|
||||
if (bios != NULL) {
|
||||
if (bios != nullptr) {
|
||||
free(bios);
|
||||
bios = NULL;
|
||||
bios = nullptr;
|
||||
}
|
||||
bios = (uint8_t*)calloc(1, 0x900);
|
||||
|
||||
if (!gbCheckRomHeader())
|
||||
return false;
|
||||
|
||||
return gbUpdateSizes();
|
||||
}
|
||||
|
||||
bool gbUpdateSizes()
|
||||
{
|
||||
if (gbRom[0x148] > 8) {
|
||||
systemMessage(MSG_UNSUPPORTED_ROM_SIZE,
|
||||
N_("Unsupported rom size %02x"), gbRom[0x148]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gbRomSize < gbRomSizes[gbRom[0x148]]) {
|
||||
uint8_t* gbRomNew = (uint8_t*)realloc(gbRom, gbRomSizes[gbRom[0x148]]);
|
||||
if (!gbRomNew) {
|
||||
assert(false);
|
||||
return false;
|
||||
};
|
||||
gbRom = gbRomNew;
|
||||
for (int i = gbRomSize; i < gbRomSizes[gbRom[0x148]]; i++)
|
||||
gbRom[i] = 0x00; // Not sure if it's 0x00, 0xff or random data...
|
||||
}
|
||||
// (it's in the case a cart is 'lying' on its size.
|
||||
else if ((gbRomSize > gbRomSizes[gbRom[0x148]]) && (genericflashcardEnable)) {
|
||||
gbRomSize = gbRomSize >> 16;
|
||||
gbRom[0x148] = 0;
|
||||
if (gbRomSize) {
|
||||
while (!((gbRomSize & 1) || (gbRom[0x148] == 7))) {
|
||||
gbRom[0x148]++;
|
||||
gbRomSize >>= 1;
|
||||
}
|
||||
gbRom[0x148]++;
|
||||
}
|
||||
uint8_t* gbRomNew = (uint8_t*)realloc(gbRom, gbRomSizes[gbRom[0x148]]);
|
||||
if (!gbRomNew) {
|
||||
assert(false);
|
||||
return false;
|
||||
};
|
||||
gbRom = gbRomNew;
|
||||
}
|
||||
gbRomSize = gbRomSizes[gbRom[0x148]];
|
||||
gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]];
|
||||
|
||||
// The 'genericflashcard' option allows some PD to work.
|
||||
// However, the setting is dangerous (if you let in enabled
|
||||
// and play a normal game, it might just break everything).
|
||||
// That's why it is not saved in the emulator options.
|
||||
// Also I added some checks in VBA to make sure your saves will not be
|
||||
// overwritten if you wrongly enable this option for a game
|
||||
// you already played (and vice-versa, ie. if you forgot to
|
||||
// enable the option for a game you played with it enabled, like Shawu Story).
|
||||
uint8_t ramsize = genericflashcardEnable ? 5 : gbRom[0x149];
|
||||
gbRom[0x149] = ramsize;
|
||||
|
||||
if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) && (gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) {
|
||||
gbCheatingDevice = 1; // GameGenie
|
||||
for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers
|
||||
gbRom[0x4000 + i] = 0;
|
||||
} else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) && (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F)))
|
||||
gbCheatingDevice = 2; // GameShark
|
||||
else
|
||||
gbCheatingDevice = 0;
|
||||
|
||||
if (ramsize > 5) {
|
||||
systemMessage(MSG_UNSUPPORTED_RAM_SIZE,
|
||||
N_("Unsupported ram size %02x"), gbRom[0x149]);
|
||||
return false;
|
||||
}
|
||||
|
||||
gbRamSize = gbRamSizes[ramsize];
|
||||
gbRamSizeMask = gbRamSizesMasks[ramsize];
|
||||
|
||||
gbRomType = gbRom[0x147];
|
||||
if (genericflashcardEnable) {
|
||||
/*if (gbRomType<2)
|
||||
gbRomType =3;
|
||||
else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) ||
|
||||
(gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d))
|
||||
gbRomType++;
|
||||
else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) ||
|
||||
(gbRomType == 0x19) || (gbRomType == 0x1c))
|
||||
gbRomType+=2;
|
||||
else if ((gbRomType == 0x5) || (gbRomType == 0x6))
|
||||
gbRomType = 0x1a;*/
|
||||
gbRomType = 0x1b;
|
||||
} else if (gbCheatingDevice == 1)
|
||||
gbRomType = 0x55;
|
||||
else if (gbCheatingDevice == 2)
|
||||
gbRomType = 0x56;
|
||||
|
||||
gbRom[0x147] = gbRomType;
|
||||
|
||||
mapperReadRAM = NULL;
|
||||
|
||||
switch (gbRomType) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
// MBC 1
|
||||
mapper = mapperMBC1ROM;
|
||||
mapperRAM = mapperMBC1RAM;
|
||||
mapperReadRAM = mapperMBC1ReadRAM;
|
||||
break;
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
// MBC2
|
||||
mapper = mapperMBC2ROM;
|
||||
mapperRAM = mapperMBC2RAM;
|
||||
gbRamSize = 0x200;
|
||||
gbRamSizeMask = 0x1ff;
|
||||
break;
|
||||
case 0x0b:
|
||||
case 0x0c:
|
||||
case 0x0d:
|
||||
// MMM01
|
||||
mapper = mapperMMM01ROM;
|
||||
mapperRAM = mapperMMM01RAM;
|
||||
break;
|
||||
case 0x0f:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
case 0xfc:
|
||||
// MBC 3
|
||||
mapper = mapperMBC3ROM;
|
||||
mapperRAM = mapperMBC3RAM;
|
||||
mapperReadRAM = mapperMBC3ReadRAM;
|
||||
break;
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
// MBC5
|
||||
mapper = mapperMBC5ROM;
|
||||
mapperRAM = mapperMBC5RAM;
|
||||
mapperReadRAM = mapperMBC5ReadRAM;
|
||||
break;
|
||||
case 0x1c:
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
// MBC 5 Rumble
|
||||
mapper = mapperMBC5ROM;
|
||||
mapperRAM = mapperMBC5RAM;
|
||||
mapperReadRAM = mapperMBC5ReadRAM;
|
||||
break;
|
||||
case 0x22:
|
||||
// MBC 7
|
||||
mapper = mapperMBC7ROM;
|
||||
mapperRAM = mapperMBC7RAM;
|
||||
mapperReadRAM = mapperMBC7ReadRAM;
|
||||
gbRamSize = 0x200;
|
||||
gbRamSizeMask = 0x1ff;
|
||||
break;
|
||||
// GG (GameGenie)
|
||||
case 0x55:
|
||||
mapper = mapperGGROM;
|
||||
break;
|
||||
case 0x56:
|
||||
// GS (GameShark)
|
||||
mapper = mapperGS3ROM;
|
||||
break;
|
||||
case 0xfd:
|
||||
// TAMA5
|
||||
if (gbRam != NULL) {
|
||||
free(gbRam);
|
||||
gbRam = NULL;
|
||||
}
|
||||
|
||||
ramsize = 3;
|
||||
gbRamSize = gbRamSizes[3];
|
||||
gbRamSizeMask = gbRamSizesMasks[3];
|
||||
gbRamFill = 0x0;
|
||||
|
||||
gbTAMA5ramSize = 0x100;
|
||||
|
||||
if (gbTAMA5ram == NULL)
|
||||
gbTAMA5ram = (uint8_t*)malloc(gbTAMA5ramSize);
|
||||
memset(gbTAMA5ram, 0x0, gbTAMA5ramSize);
|
||||
|
||||
mapperRAM = mapperTAMA5RAM;
|
||||
mapperReadRAM = mapperTAMA5ReadRAM;
|
||||
mapperUpdateClock = memoryUpdateTAMA5Clock;
|
||||
break;
|
||||
case 0xfe:
|
||||
// HuC3
|
||||
mapper = mapperHuC3ROM;
|
||||
mapperRAM = mapperHuC3RAM;
|
||||
mapperReadRAM = mapperHuC3ReadRAM;
|
||||
break;
|
||||
case 0xff:
|
||||
// HuC1
|
||||
mapper = mapperHuC1ROM;
|
||||
mapperRAM = mapperHuC1RAM;
|
||||
break;
|
||||
default:
|
||||
systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE,
|
||||
N_("Unknown cartridge type %02x"), gbRomType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gbRamSize) {
|
||||
gbRam = (uint8_t*)malloc(gbRamSize);
|
||||
memset(gbRam, gbRamFill, gbRamSize);
|
||||
}
|
||||
|
||||
switch (gbRomType) {
|
||||
case 0x03:
|
||||
case 0x06:
|
||||
case 0x0d:
|
||||
case 0x0f:
|
||||
case 0x10:
|
||||
case 0x13:
|
||||
case 0x1b:
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
case 0x22:
|
||||
case 0xfd:
|
||||
case 0xfe:
|
||||
case 0xff:
|
||||
gbBattery = 1;
|
||||
break;
|
||||
default:
|
||||
gbBattery = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (gbRomType) {
|
||||
case 0x1c:
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
gbRumble = 1;
|
||||
break;
|
||||
default:
|
||||
gbRumble = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (gbRomType) {
|
||||
case 0x0f:
|
||||
case 0x10: // mbc3
|
||||
case 0xfd: // tama5
|
||||
case 0xfe:
|
||||
gbRTCPresent = 1;
|
||||
break;
|
||||
default:
|
||||
gbRTCPresent = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
gbInit();
|
||||
|
||||
return true;
|
||||
return gbInitializeRom(romSize);
|
||||
}
|
||||
|
||||
int gbGetNextEvent(int _clockTicks)
|
||||
|
@ -5593,32 +5448,47 @@ void gbEmulate(int ticksToStop)
|
|||
}
|
||||
}
|
||||
|
||||
bool gbLoadRomData(const char* data, unsigned size)
|
||||
{
|
||||
gbRomSize = size;
|
||||
if (gbRom != NULL) {
|
||||
bool gbLoadRomData(const char* data, size_t size) {
|
||||
if (gbRom != nullptr) {
|
||||
gbCleanUp();
|
||||
}
|
||||
|
||||
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
||||
|
||||
gbRom = (uint8_t*)calloc(1, gbRomSize);
|
||||
if (gbRom == NULL) {
|
||||
gbRom = (uint8_t*)calloc(1, size);
|
||||
if (gbRom == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(gbRom, data, gbRomSize);
|
||||
memcpy(gbRom, data, size);
|
||||
|
||||
gbBatteryError = false;
|
||||
|
||||
if (bios != NULL) {
|
||||
if (bios != nullptr) {
|
||||
free(bios);
|
||||
bios = NULL;
|
||||
bios = nullptr;
|
||||
}
|
||||
|
||||
bios = (uint8_t*)calloc(1, 0x900);
|
||||
return gbUpdateSizes();
|
||||
return gbInitializeRom(size);
|
||||
}
|
||||
|
||||
#ifndef __LIBRETRO__
|
||||
bool gbApplyPatch(const char* patchName) {
|
||||
int size = g_gbCartData.rom_size();
|
||||
if (!applyPatch(patchName, &gbRom, &size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t newSize = size;
|
||||
if (newSize != g_gbCartData.rom_size()) {
|
||||
return gbInitializeRom(newSize);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // __LIBRETRO__
|
||||
|
||||
#ifdef __LIBRETRO__
|
||||
#include <stddef.h>
|
||||
|
||||
|
@ -5651,7 +5521,7 @@ unsigned int gbWriteSaveState(uint8_t* data)
|
|||
utilWriteMem(data, &gbRTCHuC3, sizeof(gbRTCHuC3));
|
||||
utilWriteMem(data, &gbDataTAMA5, sizeof(gbDataTAMA5));
|
||||
if (gbTAMA5ram != NULL)
|
||||
utilWriteMem(data, gbTAMA5ram, gbTAMA5ramSize);
|
||||
utilWriteMem(data, gbTAMA5ram, kTama5RamSize);
|
||||
utilWriteMem(data, &gbDataMMM01, sizeof(gbDataMMM01));
|
||||
|
||||
utilWriteMem(data, gbPalette, 128 * sizeof(uint16_t));
|
||||
|
@ -5770,7 +5640,7 @@ bool gbReadSaveState(const uint8_t* data)
|
|||
utilReadMem(&gbRTCHuC3, data, sizeof(gbRTCHuC3));
|
||||
utilReadMem(&gbDataTAMA5, data, sizeof(gbDataTAMA5));
|
||||
if (gbTAMA5ram != NULL) {
|
||||
utilReadMem(gbTAMA5ram, data, gbTAMA5ramSize);
|
||||
utilReadMem(gbTAMA5ram, data, kTama5RamSize);
|
||||
}
|
||||
utilReadMem(&gbDataMMM01, data, sizeof(gbDataMMM01));
|
||||
|
||||
|
|
19
src/gb/gb.h
19
src/gb/gb.h
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
#include "gbCartData.h"
|
||||
|
||||
#define gbWidth 160
|
||||
#define gbHeight 144
|
||||
#define sgbWidth 256
|
||||
|
@ -28,8 +30,18 @@ extern gbRegister AF, BC, DE, HL, SP, PC;
|
|||
extern uint16_t IFF;
|
||||
int gbDis(char*, uint16_t);
|
||||
|
||||
bool gbLoadRom(const char*);
|
||||
bool gbUpdateSizes();
|
||||
// Attempts to load the ROM file at `filename`. Returns true on success.
|
||||
bool gbLoadRom(const char* filename);
|
||||
// Attempts to load the ROM at `romData`, with a size of `romSize`. This will
|
||||
// make a copy of `romData`. Returns true on success.
|
||||
bool gbLoadRomData(const char* romData, size_t romSize);
|
||||
|
||||
#ifndef __LIBRETRO__
|
||||
// Attempts to apply `patchName` to the currently loaded ROM. Returns true on
|
||||
// success.
|
||||
bool gbApplyPatch(const char* patchName);
|
||||
#endif // __LIBRETRO__
|
||||
|
||||
void gbEmulate(int);
|
||||
void gbWriteMemory(uint16_t, uint8_t);
|
||||
void gbDrawLine();
|
||||
|
@ -61,8 +73,6 @@ bool gbWritePNGFile(const char*);
|
|||
bool gbWriteBMPFile(const char*);
|
||||
bool gbReadGSASnapshot(const char*);
|
||||
|
||||
bool gbLoadRomData(const char* data, unsigned size);
|
||||
|
||||
// Allows invalid vram/palette access needed for Colorizer hacked games in GBC/GBA hardware
|
||||
void setColorizerHack(bool value);
|
||||
bool allowColorizerHack(void);
|
||||
|
@ -72,6 +82,7 @@ extern int gbRomType; // gets type from header 0x147
|
|||
extern int gbBattery; // enabled when gbRamSize != 0
|
||||
extern int gbRTCPresent; // gbROM has RTC support
|
||||
|
||||
extern gbCartData g_gbCartData;
|
||||
extern struct EmulatedSystem GBSystem;
|
||||
|
||||
#endif // GB_H
|
||||
|
|
|
@ -0,0 +1,431 @@
|
|||
#include "gbCartData.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
#include "../common/Port.h"
|
||||
#include "../common/sizes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// Packed struct for the ROM header.
|
||||
struct gbRomHeader {
|
||||
uint8_t header[0x100];
|
||||
// Entry point starts at 0x100.
|
||||
uint8_t entry_point[4];
|
||||
uint8_t nintendo_logo[0x30];
|
||||
union {
|
||||
uint8_t old_title[16];
|
||||
struct {
|
||||
uint8_t title[11];
|
||||
uint8_t manufacturer_code[4];
|
||||
uint8_t cgb_flag;
|
||||
} new_format;
|
||||
};
|
||||
uint8_t new_licensee_code[2];
|
||||
uint8_t sgb_flag;
|
||||
uint8_t cartridge_type;
|
||||
uint8_t rom_size;
|
||||
uint8_t ram_size;
|
||||
uint8_t destination_code;
|
||||
uint8_t old_licensee_code;
|
||||
uint8_t version_number;
|
||||
uint8_t header_checksum;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
char byte_to_char(uint8_t byte) {
|
||||
if (byte < 10) {
|
||||
return '0' + byte;
|
||||
} else if (byte < 16) {
|
||||
return 'A' + (byte - 10);
|
||||
} else {
|
||||
assert(false);
|
||||
return '\0';
|
||||
}
|
||||
}
|
||||
|
||||
std::string old_licensee_to_string(uint8_t licensee) {
|
||||
const std::array<char, 2> result = {
|
||||
byte_to_char(licensee / 16),
|
||||
byte_to_char(licensee % 16),
|
||||
};
|
||||
return std::string(result.begin(), result.end());
|
||||
}
|
||||
|
||||
constexpr size_t kHeaderGlobalChecksumAdress = 0x14e;
|
||||
|
||||
uint16_t get_rom_checksum(const uint8_t* romData, size_t romDataSize) {
|
||||
assert(romData);
|
||||
|
||||
uint16_t checksum = 0;
|
||||
for (size_t i = 0; i < romDataSize; i++) {
|
||||
// Skip over the global checksum bytes.
|
||||
if (i == kHeaderGlobalChecksumAdress ||
|
||||
i == kHeaderGlobalChecksumAdress + 1) {
|
||||
continue;
|
||||
}
|
||||
checksum += romData[i];
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
bool is_game_genie(const gbRomHeader* romHeader) {
|
||||
static constexpr std::array<uint8_t, 16> kGameGenieHeader = {
|
||||
0x47, 0x61, 0x6d, 0x65, 0x20, 0x47, 0x65, 0x6e,
|
||||
0x69, 0x65, 0x28, 0x54, 0x4d, 0x29, 0x20, 0x73,
|
||||
};
|
||||
|
||||
return std::equal(romHeader->header,
|
||||
romHeader->header + kGameGenieHeader.size(),
|
||||
kGameGenieHeader.begin(), kGameGenieHeader.end());
|
||||
}
|
||||
|
||||
bool is_game_shark(const gbRomHeader* romHeader) {
|
||||
static constexpr std::array<uint8_t, 14> kGameSharkTitle = {
|
||||
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x20};
|
||||
|
||||
return std::equal(romHeader->old_title,
|
||||
romHeader->old_title + kGameSharkTitle.size(),
|
||||
kGameSharkTitle.begin(), kGameSharkTitle.end());
|
||||
}
|
||||
|
||||
bool is_nintendo_logo_valid(const gbRomHeader* romHeader) {
|
||||
static constexpr std::array<uint8_t, 0x30> kNintendoLogo = {
|
||||
0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b, 0x03, 0x73, 0x00, 0x83,
|
||||
0x00, 0x0c, 0x00, 0x0d, 0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e,
|
||||
0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99, 0xbb, 0xbb, 0x67, 0x63,
|
||||
0x6e, 0x0e, 0xec, 0xcc, 0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e,
|
||||
};
|
||||
|
||||
return std::equal(romHeader->nintendo_logo,
|
||||
romHeader->nintendo_logo + kNintendoLogo.size(),
|
||||
kNintendoLogo.begin(), kNintendoLogo.end());
|
||||
}
|
||||
|
||||
constexpr uint8_t kNewManufacturerCode = 0x33;
|
||||
constexpr uint8_t kSgbSetFlag = 0x03;
|
||||
constexpr uint8_t kCgbSupportedFlag = 0x80;
|
||||
constexpr uint8_t kCgbRequiredFlag = 0xc0;
|
||||
|
||||
// The start and end addresses of the cartridge header. This is used for the
|
||||
// header checksum calculation.
|
||||
constexpr size_t kHeaderChecksumStartAdress = 0x134;
|
||||
constexpr size_t kHeaderChecksumEndAdress = 0x14c;
|
||||
|
||||
} // namespace
|
||||
|
||||
gbCartData::gbCartData(const uint8_t* romData, size_t romDataSize) {
|
||||
assert(romData);
|
||||
|
||||
if (romDataSize < sizeof(gbRomHeader)) {
|
||||
validity_ = Validity::kSizeTooSmall;
|
||||
return;
|
||||
}
|
||||
|
||||
const gbRomHeader* header = (gbRomHeader*)romData;
|
||||
|
||||
if (is_game_genie(header)) {
|
||||
// Game Genie is special.
|
||||
title_ = "Game Genie";
|
||||
header_type_ = RomHeaderType::kOldLicenseeCode;
|
||||
mapper_flag_ = 0x55;
|
||||
mapper_type_ = MapperType::kGameGenie;
|
||||
rom_size_ = k32KiB;
|
||||
rom_mask_ = rom_size_ -1;
|
||||
validity_ = Validity::kValid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_game_shark(header)) {
|
||||
// Game Shark is special.
|
||||
title_ = "Action Replay";
|
||||
header_type_ = RomHeaderType::kOldLicenseeCode;
|
||||
mapper_flag_ = 0x56;
|
||||
mapper_type_ = MapperType::kGameShark;
|
||||
rom_size_ = k32KiB;
|
||||
rom_mask_ = rom_size_ -1;
|
||||
validity_ = Validity::kValid;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!is_nintendo_logo_valid(header)) {
|
||||
validity_ = Validity::kNoNintendoLogo;
|
||||
return;
|
||||
}
|
||||
|
||||
old_licensee_code_ = header->old_licensee_code;
|
||||
if (old_licensee_code_ == kNewManufacturerCode) {
|
||||
// New licensee code format.
|
||||
header_type_ = RomHeaderType::kNewLicenseeCode;
|
||||
title_ =
|
||||
std::string(reinterpret_cast<const char*>(header->new_format.title),
|
||||
sizeof(header->new_format.title));
|
||||
maker_code_ = std::string(
|
||||
reinterpret_cast<const char*>(header->new_licensee_code),
|
||||
sizeof(header->new_licensee_code));
|
||||
manufacturer_code_ = std::string(
|
||||
reinterpret_cast<const char*>(header->new_format.manufacturer_code),
|
||||
sizeof(header->new_format.manufacturer_code));
|
||||
|
||||
// CGB support flag.
|
||||
cgb_flag_ = header->new_format.cgb_flag;
|
||||
if (cgb_flag_ == kCgbSupportedFlag) {
|
||||
cgb_support_ = CGBSupport::kSupported;
|
||||
} else if (cgb_flag_ == kCgbRequiredFlag) {
|
||||
cgb_support_ = CGBSupport::kRequired;
|
||||
} else {
|
||||
cgb_support_ = CGBSupport::kNone;
|
||||
}
|
||||
} else {
|
||||
// Old licensee code format.
|
||||
header_type_ = RomHeaderType::kOldLicenseeCode;
|
||||
|
||||
title_ = std::string(reinterpret_cast<const char*>(header->old_title),
|
||||
sizeof(header->old_title));
|
||||
|
||||
maker_code_ = old_licensee_to_string(header->old_licensee_code);
|
||||
|
||||
cgb_flag_ = 0;
|
||||
cgb_support_ = CGBSupport::kNone;
|
||||
manufacturer_code_ = "";
|
||||
}
|
||||
|
||||
// SGB support flag.
|
||||
sgb_flag_ = header->sgb_flag;
|
||||
sgb_support_ = header->sgb_flag == kSgbSetFlag;
|
||||
|
||||
// We may have to skip the RAM size calculation for some mappers.
|
||||
bool skip_ram = false;
|
||||
mapper_flag_ = header->cartridge_type;
|
||||
switch (mapper_flag_) {
|
||||
case 0x00:
|
||||
mapper_type_ = MapperType::kNone;
|
||||
break;
|
||||
case 0x01:
|
||||
mapper_type_ = MapperType::kMbc1;
|
||||
break;
|
||||
case 0x02:
|
||||
mapper_type_ = MapperType::kMbc1;
|
||||
break;
|
||||
case 0x03:
|
||||
mapper_type_ = MapperType::kMbc1;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x05:
|
||||
// MBC2 header does not specify a RAM size so set it here.
|
||||
mapper_type_ = MapperType::kMbc2;
|
||||
ram_size_ = k512B;
|
||||
skip_ram = true;
|
||||
break;
|
||||
case 0x06:
|
||||
mapper_type_ = MapperType::kMbc2;
|
||||
ram_size_ = k512B;
|
||||
skip_ram = true;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x08:
|
||||
mapper_type_ = MapperType::kNone;
|
||||
break;
|
||||
case 0x09:
|
||||
mapper_type_ = MapperType::kNone;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x0b:
|
||||
mapper_type_ = MapperType::kMmm01;
|
||||
break;
|
||||
case 0x0c:
|
||||
mapper_type_ = MapperType::kMmm01;
|
||||
break;
|
||||
case 0x0d:
|
||||
mapper_type_ = MapperType::kMmm01;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x0f:
|
||||
mapper_type_ = MapperType::kMbc3;
|
||||
has_rtc_ = true;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x10:
|
||||
mapper_type_ = MapperType::kMbc3;
|
||||
has_rtc_ = true;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x11:
|
||||
mapper_type_ = MapperType::kMbc3;
|
||||
break;
|
||||
case 0x12:
|
||||
mapper_type_ = MapperType::kMbc3;
|
||||
break;
|
||||
case 0x13:
|
||||
mapper_type_ = MapperType::kMbc3;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x19:
|
||||
mapper_type_ = MapperType::kMbc5;
|
||||
break;
|
||||
case 0x1a:
|
||||
mapper_type_ = MapperType::kMbc5;
|
||||
break;
|
||||
case 0x1b:
|
||||
mapper_type_ = MapperType::kMbc5;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
case 0x1c:
|
||||
mapper_type_ = MapperType::kMbc5;
|
||||
has_rumble_ = true;
|
||||
break;
|
||||
case 0x1d:
|
||||
mapper_type_ = MapperType::kMbc5;
|
||||
has_rumble_ = true;
|
||||
break;
|
||||
case 0x1e:
|
||||
mapper_type_ = MapperType::kMbc5;
|
||||
has_battery_ = true;
|
||||
has_rumble_ = true;
|
||||
break;
|
||||
case 0x20:
|
||||
mapper_type_ = MapperType::kMbc6;
|
||||
break;
|
||||
case 0x22:
|
||||
// MBC7 header does not specify a RAM size so set it here.
|
||||
mapper_type_ = MapperType::kMbc7;
|
||||
ram_size_ = k512B;
|
||||
skip_ram = true;
|
||||
has_battery_ = true;
|
||||
has_rumble_ = true;
|
||||
has_sensor_ = true;
|
||||
break;
|
||||
case 0x55:
|
||||
mapper_type_ = MapperType::kGameGenie;
|
||||
break;
|
||||
case 0x56:
|
||||
mapper_type_ = MapperType::kGameShark;
|
||||
break;
|
||||
case 0xfc:
|
||||
mapper_type_ = MapperType::kPocketCamera;
|
||||
break;
|
||||
case 0xfd:
|
||||
// Tama5 header does not specify a RAM size so set it here.
|
||||
mapper_type_ = MapperType::kTama5;
|
||||
ram_size_ = k32KiB;
|
||||
skip_ram = true;
|
||||
has_battery_ = true;
|
||||
has_rtc_ = true;
|
||||
break;
|
||||
case 0xfe:
|
||||
mapper_type_ = MapperType::kHuC3;
|
||||
has_battery_ = true;
|
||||
has_rtc_ = true;
|
||||
break;
|
||||
case 0xff:
|
||||
mapper_type_ = MapperType::kHuC1;
|
||||
has_battery_ = true;
|
||||
break;
|
||||
default:
|
||||
validity_ = Validity::kUnknownMapperType;
|
||||
return;
|
||||
}
|
||||
|
||||
rom_flag_ = header->rom_size;
|
||||
switch (rom_flag_) {
|
||||
case 0x00:
|
||||
rom_size_ = k32KiB;
|
||||
break;
|
||||
case 0x01:
|
||||
rom_size_ = k64KiB;
|
||||
break;
|
||||
case 0x02:
|
||||
rom_size_ = k128KiB;
|
||||
break;
|
||||
case 0x03:
|
||||
rom_size_ = k256KiB;
|
||||
break;
|
||||
case 0x04:
|
||||
rom_size_ = k512KiB;
|
||||
break;
|
||||
case 0x05:
|
||||
rom_size_ = k1MiB;
|
||||
break;
|
||||
case 0x06:
|
||||
rom_size_ = k2MiB;
|
||||
break;
|
||||
case 0x07:
|
||||
rom_size_ = k4MiB;
|
||||
break;
|
||||
case 0x08:
|
||||
rom_size_ = k8MiB;
|
||||
break;
|
||||
default:
|
||||
validity_ = Validity::kUnknownRomSize;
|
||||
return;
|
||||
}
|
||||
|
||||
// The ROM mask is always rom_size_ - 1.
|
||||
rom_mask_ = rom_size_ - 1;
|
||||
|
||||
ram_flag_ = header->ram_size;
|
||||
if (!skip_ram) {
|
||||
switch (ram_flag_) {
|
||||
case 0x00:
|
||||
ram_size_ = 0;
|
||||
break;
|
||||
case 0x01:
|
||||
ram_size_ = k2KiB;
|
||||
break;
|
||||
case 0x02:
|
||||
ram_size_ = k8KiB;
|
||||
break;
|
||||
case 0x03:
|
||||
ram_size_ = k32KiB;
|
||||
break;
|
||||
case 0x04:
|
||||
ram_size_ = k128KiB;
|
||||
break;
|
||||
case 0x05:
|
||||
ram_size_ = k64KiB;
|
||||
break;
|
||||
default:
|
||||
validity_ = Validity::kUnknownRamSize;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ram_size_ != 0) {
|
||||
ram_mask_ = ram_size_ - 1;
|
||||
}
|
||||
|
||||
version_flag_ = header->version_number;
|
||||
|
||||
// Calculate the header checksum.
|
||||
header_checksum_ = header->header_checksum;
|
||||
uint8_t checksum = 0;
|
||||
for (uint16_t address = kHeaderChecksumStartAdress;
|
||||
address <= kHeaderChecksumEndAdress; address++) {
|
||||
checksum = checksum - romData[address] - 1;
|
||||
}
|
||||
actual_header_checksum_ = checksum;
|
||||
|
||||
if (header_checksum_ != actual_header_checksum_) {
|
||||
validity_ = Validity::kInvalidHeaderChecksum;
|
||||
return;
|
||||
}
|
||||
|
||||
// The global checksum is stored in big endian.
|
||||
global_checksum_ = *(romData + kHeaderGlobalChecksumAdress) * 0x100 +
|
||||
*(romData + kHeaderGlobalChecksumAdress + 1);
|
||||
|
||||
// The global checksum value is computed here. This does not fail the header
|
||||
// verification as it does not on actual hardware either.
|
||||
actual_global_checksum_ = get_rom_checksum(romData, romDataSize);
|
||||
|
||||
validity_ = Validity::kValid;
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
#ifndef VBAM_GB_GB_ROM_H_
|
||||
#define VBAM_GB_GB_ROM_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class gbCartData final {
|
||||
public:
|
||||
enum class RomHeaderType {
|
||||
kOldLicenseeCode,
|
||||
kNewLicenseeCode,
|
||||
kInvalid,
|
||||
};
|
||||
|
||||
enum class MapperType {
|
||||
kNone,
|
||||
kMbc1,
|
||||
kMbc2,
|
||||
kMbc3,
|
||||
kMbc5,
|
||||
kMbc6,
|
||||
kMbc7,
|
||||
kPocketCamera,
|
||||
kMmm01,
|
||||
kHuC1,
|
||||
kHuC3,
|
||||
kTama5,
|
||||
kGameGenie,
|
||||
kGameShark,
|
||||
kUnknown,
|
||||
};
|
||||
|
||||
enum class CGBSupport {
|
||||
kNone,
|
||||
kSupported,
|
||||
kRequired,
|
||||
};
|
||||
|
||||
enum class DestinationCode {
|
||||
kJapanese,
|
||||
kWorldwide,
|
||||
kUnknown,
|
||||
};
|
||||
|
||||
enum class Validity {
|
||||
kValid,
|
||||
// This object is uninitialized (default state).
|
||||
kUninitialized,
|
||||
// The provided size is too small to contain a valid header.
|
||||
kSizeTooSmall,
|
||||
// The mapper code is unknown. mapper_flag() will be set.
|
||||
kUnknownMapperType,
|
||||
// The ROM size byte is unknown. rom_flag() will be set.
|
||||
kUnknownRomSize,
|
||||
// The RAM size byte is unknown. ram_flag() will be set.
|
||||
kUnknownRamSize,
|
||||
// The Nintendo logo is invalid.
|
||||
kNoNintendoLogo,
|
||||
// The header checksum is invalid. header_checksum() and
|
||||
// actual_header_checksum() will be set.
|
||||
kInvalidHeaderChecksum,
|
||||
};
|
||||
|
||||
gbCartData() = default;
|
||||
gbCartData(const uint8_t* romData, size_t romDataSize);
|
||||
~gbCartData() = default;
|
||||
|
||||
// Whether CGB is supported in any form.
|
||||
bool SupportsCGB() const {
|
||||
return cgb_support_ == CGBSupport::kSupported ||
|
||||
cgb_support_ == CGBSupport::kRequired;
|
||||
};
|
||||
|
||||
// Whether CGB support is required.
|
||||
bool RequiresCGB() const { return cgb_support_ == CGBSupport::kRequired; };
|
||||
|
||||
// Whether this ROM is valid.
|
||||
bool IsValid() const { return validity_ == Validity::kValid; }
|
||||
operator bool() const { return IsValid(); }
|
||||
|
||||
// Whether this cartridge has RAM.
|
||||
bool HasRam() const { return ram_size() != 0; }
|
||||
|
||||
// The Validity of this object. Doubles as an error code. If this does not
|
||||
// return kValid, this object may be in a half-initialized state and no
|
||||
// other accessor should be considered to return valid data.
|
||||
Validity validity() const { return validity_; }
|
||||
|
||||
// The Game Boy cartridge header type.
|
||||
RomHeaderType header_type() const { return header_type_; }
|
||||
|
||||
// The old licensee code in the ROM header (0x014b). Set to 0x33 if
|
||||
// header_type() is kNewLicenseeCode.
|
||||
uint8_t old_licensee_code() const { return old_licensee_code_; }
|
||||
// The ROM title.
|
||||
const std::string& title() const { return title_; }
|
||||
// The 2-characters Maker code.
|
||||
const std::string& maker_code() const { return maker_code_; }
|
||||
// The 4-characters manufacturer code.
|
||||
// Only set if header_type() is kNewLicenseeCode. Empty string otherwise.
|
||||
const std::string& manufacturer_code() const { return manufacturer_code_; }
|
||||
|
||||
// The mapper flag in the ROM header (0x0147).
|
||||
uint8_t mapper_flag() const { return mapper_flag_; }
|
||||
// The cartridge mapper type.
|
||||
MapperType mapper_type() const { return mapper_type_; }
|
||||
|
||||
// Whether the cartridge has battery support.
|
||||
bool has_battery() const { return has_battery_; }
|
||||
// Whether the cartridge has RTC support.
|
||||
bool has_rtc() const { return has_rtc_; }
|
||||
// Whether the cartridge has rumble support.
|
||||
bool has_rumble() const { return has_rumble_; }
|
||||
// Whether the cartridge has sensor support (accelerometer).
|
||||
bool has_sensor() const { return has_sensor_; }
|
||||
|
||||
// The CGB flag in the ROM header (0x0143).
|
||||
uint8_t cgb_flag() const { return cgb_flag_; }
|
||||
// Whether the ROM supports or requires CGB.
|
||||
CGBSupport cgb_support() const { return cgb_support_; }
|
||||
|
||||
// The SGB flag in the ROM header (0x0146).
|
||||
uint8_t sgb_flag() const { return sgb_flag_; }
|
||||
// Whether the ROM supports SGB.
|
||||
bool sgb_support() const { return sgb_support_; }
|
||||
|
||||
// The ROM size flag in the ROM header (0x0148).
|
||||
uint8_t rom_flag() const { return rom_flag_; }
|
||||
// The ROM size, in bytes.
|
||||
size_t rom_size() const { return rom_size_; }
|
||||
// The ROM mask.
|
||||
size_t rom_mask() const { return rom_mask_; }
|
||||
|
||||
// The RAM size flag in the ROM header (0x0149).
|
||||
uint8_t ram_flag() const { return ram_flag_; }
|
||||
// The RAM size, in bytes.
|
||||
size_t ram_size() const { return ram_size_; }
|
||||
// The RAM mask.
|
||||
size_t ram_mask() const { return ram_mask_; }
|
||||
|
||||
// The destination code flag in the ROM header (0x014a).
|
||||
uint8_t destination_code_flag() const { return destination_code_flag_; }
|
||||
// The destination code.
|
||||
DestinationCode destination_code() const { return destination_code_; }
|
||||
|
||||
// The version flag in the ROM header (0x014c).
|
||||
uint8_t version_flag() const { return version_flag_; }
|
||||
|
||||
// The advertised header checksum in the ROM header (0x014d).
|
||||
uint8_t header_checksum() const { return header_checksum_; }
|
||||
// The actual header checksum.
|
||||
uint8_t actual_header_checksum() const { return actual_header_checksum_; }
|
||||
|
||||
// The advertised global checksum in the ROM header (0x014e-0x014f).
|
||||
uint16_t global_checksum() const { return global_checksum_; }
|
||||
// The actual global checksum. This is never checked on real hardware.
|
||||
uint16_t actual_global_checksum() const { return actual_global_checksum_; }
|
||||
|
||||
private:
|
||||
Validity validity_ = Validity::kUninitialized;
|
||||
|
||||
RomHeaderType header_type_ = RomHeaderType::kInvalid;
|
||||
|
||||
uint8_t old_licensee_code_ = 0;
|
||||
std::string title_ = "";
|
||||
std::string maker_code_ = "";
|
||||
std::string manufacturer_code_ = "";
|
||||
|
||||
uint8_t mapper_flag_ = 0;
|
||||
MapperType mapper_type_ = MapperType::kUnknown;
|
||||
|
||||
bool has_battery_ = false;
|
||||
bool has_rtc_ = false;
|
||||
bool has_rumble_ = false;
|
||||
bool has_sensor_ = false;
|
||||
|
||||
uint8_t cgb_flag_ = 0;
|
||||
CGBSupport cgb_support_ = CGBSupport::kNone;
|
||||
|
||||
uint8_t sgb_flag_ = 0;
|
||||
bool sgb_support_ = false;
|
||||
|
||||
uint8_t rom_flag_ = 0;
|
||||
size_t rom_size_ = 0;
|
||||
size_t rom_mask_ = 0;
|
||||
|
||||
uint8_t ram_flag_ = 0;
|
||||
size_t ram_size_ = 0;
|
||||
size_t ram_mask_ = 0;
|
||||
|
||||
uint8_t destination_code_flag_ = 0;
|
||||
DestinationCode destination_code_ = DestinationCode::kUnknown;
|
||||
|
||||
uint8_t version_flag_ = 0;
|
||||
|
||||
uint8_t header_checksum_ = 0;
|
||||
uint8_t actual_header_checksum_ = 0;
|
||||
|
||||
uint16_t global_checksum_ = 0;
|
||||
uint16_t actual_global_checksum_ = 0;
|
||||
};
|
||||
|
||||
#endif // VBAM_GB_GB_ROM_H_
|
|
@ -7,7 +7,6 @@ int gbRomSizeMask = 0;
|
|||
int gbRomSize = 0;
|
||||
int gbRamSizeMask = 0;
|
||||
int gbRamSize = 0;
|
||||
int gbTAMA5ramSize = 0;
|
||||
|
||||
uint8_t* gbMemory = NULL;
|
||||
uint8_t* gbVram = NULL;
|
||||
|
|
|
@ -7,7 +7,6 @@ extern int gbRomSizeMask;
|
|||
extern int gbRomSize;
|
||||
extern int gbRamSize;
|
||||
extern int gbRamSizeMask;
|
||||
extern int gbTAMA5ramSize;
|
||||
|
||||
extern uint8_t* bios;
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ SOURCES_CXX += \
|
|||
$(CORE_DIR)/gba/Sram.cpp
|
||||
|
||||
SOURCES_CXX += \
|
||||
$(CORE_DIR)/gb/gbCartData.cpp \
|
||||
$(CORE_DIR)/gb/gbCheats.cpp \
|
||||
$(CORE_DIR)/gb/GB.cpp \
|
||||
$(CORE_DIR)/gb/gbGfx.cpp \
|
||||
|
|
|
@ -1725,15 +1725,9 @@ int main(int argc, char** argv)
|
|||
|
||||
cartridgeType = IMAGE_GB;
|
||||
emulator = GBSystem;
|
||||
int size = gbRomSize, patchnum;
|
||||
for (patchnum = 0; patchnum < patchNum; patchnum++) {
|
||||
for (int patchnum = 0; patchnum < patchNum; patchnum++) {
|
||||
fprintf(stdout, "Trying patch %s%s\n", patchNames[patchnum],
|
||||
applyPatch(patchNames[patchnum], &gbRom, &size) ? " [success]" : "");
|
||||
}
|
||||
if (size != gbRomSize) {
|
||||
extern bool gbUpdateSizes();
|
||||
gbUpdateSizes();
|
||||
gbReset();
|
||||
gbApplyPatch(patchNames[patchnum]) ? " [success]" : "");
|
||||
}
|
||||
gbReset();
|
||||
}
|
||||
|
|
|
@ -238,16 +238,8 @@ void GameArea::LoadGame(const wxString& name)
|
|||
return;
|
||||
}
|
||||
|
||||
rom_size = gbRomSize;
|
||||
|
||||
if (loadpatch) {
|
||||
int size = rom_size;
|
||||
applyPatch(UTF8(pfn.GetFullPath()), &gbRom, &size);
|
||||
|
||||
if (size != (int)rom_size)
|
||||
gbUpdateSizes();
|
||||
|
||||
rom_size = size;
|
||||
gbApplyPatch(UTF8(pfn.GetFullPath()));
|
||||
}
|
||||
|
||||
// start sound; this must happen before CPU stuff
|
||||
|
|
Loading…
Reference in New Issue