mirror of https://github.com/mgba-emu/mgba.git
GBA Memory: Add initial support for N-in-1 style multicarts
This commit is contained in:
parent
d5fbd0ff1c
commit
91cf829261
|
@ -0,0 +1,75 @@
|
|||
/* Copyright (c) 2013-2024 Jeffrey Pfau
|
||||
* Copyright (c) 2016 taizou
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef GBA_UNLICENSED_H
|
||||
#define GBA_UNLICENSED_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/timing.h>
|
||||
|
||||
enum GBAVFameCartType {
|
||||
VFAME_STANDARD = 0,
|
||||
VFAME_GEORGE = 1,
|
||||
VFAME_ALTERNATE = 2,
|
||||
};
|
||||
|
||||
enum GBAUnlCartType {
|
||||
GBA_UNL_CART_NONE = 0,
|
||||
GBA_UNL_CART_VFAME = 1,
|
||||
GBA_UNL_CART_MULTICART = 2,
|
||||
};
|
||||
|
||||
struct GBAVFameCart {
|
||||
enum GBAVFameCartType cartType;
|
||||
int sramMode;
|
||||
int romMode;
|
||||
int8_t writeSequence[5];
|
||||
bool acceptingModeChange;
|
||||
};
|
||||
|
||||
struct GBAMulticart {
|
||||
struct mTimingEvent settle;
|
||||
uint32_t* rom;
|
||||
size_t fullSize;
|
||||
|
||||
uint8_t bank;
|
||||
uint8_t offset;
|
||||
uint8_t size;
|
||||
bool sramActive;
|
||||
uint8_t unk;
|
||||
};
|
||||
|
||||
struct GBAUnlCart {
|
||||
enum GBAUnlCartType type;
|
||||
union {
|
||||
struct GBAVFameCart vfame;
|
||||
struct GBAMulticart multi;
|
||||
};
|
||||
};
|
||||
|
||||
struct GBA;
|
||||
struct GBAMemory;
|
||||
void GBAUnlCartInit(struct GBA*);
|
||||
void GBAUnlCartReset(struct GBA*);
|
||||
void GBAUnlCartUnload(struct GBA*);
|
||||
void GBAUnlCartDetect(struct GBA*);
|
||||
void GBAUnlCartWriteSRAM(struct GBA*, uint32_t address, uint8_t value);
|
||||
|
||||
struct GBASerializedState;
|
||||
void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state);
|
||||
void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* state);
|
||||
|
||||
bool GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32);
|
||||
void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData);
|
||||
uint32_t GBAVFameModifyRomAddress(struct GBAVFameCart* cart, uint32_t address, size_t romSize);
|
||||
uint32_t GBAVFameGetPatternValue(uint32_t address, int bits);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -1,41 +0,0 @@
|
|||
/* Copyright (c) 2016 taizou
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Support for copy protected unlicensed games from the company Vast Fame
|
||||
|
||||
#ifndef GBA_VFAME_H
|
||||
#define GBA_VFAME_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#define DIGIMON_SAPPHIRE_CHINESE_CRC32 0x793A328F
|
||||
|
||||
enum GBAVFameCartType {
|
||||
VFAME_NO = 0,
|
||||
VFAME_STANDARD = 1,
|
||||
VFAME_GEORGE = 2,
|
||||
VFAME_ALTERNATE = 3,
|
||||
};
|
||||
|
||||
struct GBAVFameCart {
|
||||
enum GBAVFameCartType cartType;
|
||||
int sramMode;
|
||||
int romMode;
|
||||
int8_t writeSequence[5];
|
||||
bool acceptingModeChange;
|
||||
};
|
||||
|
||||
void GBAVFameInit(struct GBAVFameCart* cart);
|
||||
void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32);
|
||||
void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData);
|
||||
uint32_t GBAVFameModifyRomAddress(struct GBAVFameCart* cart, uint32_t address, size_t romSize);
|
||||
uint32_t GBAVFameGetPatternValue(uint32_t address, int bits);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -18,7 +18,7 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/gba/cart/ereader.h>
|
||||
#include <mgba/internal/gba/cart/gpio.h>
|
||||
#include <mgba/internal/gba/cart/matrix.h>
|
||||
#include <mgba/internal/gba/cart/vfame.h>
|
||||
#include <mgba/internal/gba/cart/unlicensed.h>
|
||||
|
||||
enum GBAMemoryRegion {
|
||||
GBA_REGION_BIOS = 0x0,
|
||||
|
@ -108,8 +108,8 @@ struct GBAMemory {
|
|||
|
||||
struct GBACartridgeHardware hw;
|
||||
struct GBASavedata savedata;
|
||||
struct GBAVFameCart vfame;
|
||||
struct GBAMatrix matrix;
|
||||
struct GBAUnlCart unl;
|
||||
struct GBACartEReader ereader;
|
||||
size_t romSize;
|
||||
uint32_t romMask;
|
||||
|
|
|
@ -186,11 +186,14 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | bit 3: Reserved
|
||||
* | bits 4 - 15: Light counter
|
||||
* | 0x002C0 - 0x002C0: Light sample
|
||||
* | 0x002C1 - 0x002C3: Flags
|
||||
* | 0x002C1: Flags
|
||||
* | bits 0 - 1: Tilt state machine
|
||||
* | bits 2 - 3: GB Player inputs posted
|
||||
* | bits 4 - 8: GB Player transmit position
|
||||
* | bits 9 - 23: Reserved
|
||||
* | bits 4 - 7: GB Player transmit position
|
||||
* | 0x002C2 - 0x002C3: Unlicensed cart flags
|
||||
* | bits 0 - 4: Cartridge type
|
||||
* | bits 5 - 7: Cartridge subtype
|
||||
* | bits 8 - 15: Reserved
|
||||
* 0x002C4 - 0x002C7: SIO next event
|
||||
* 0x002C8 - 0x002CB: Current DMA transfer word
|
||||
* 0x002CC - 0x002CF: Last DMA transfer PC
|
||||
|
@ -230,8 +233,23 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | bits 15 - 31: Reserved
|
||||
* 0x00320 - 0x00323: Next IRQ event
|
||||
* 0x00324 - 0x00327: Interruptable BIOS stall cycles
|
||||
* 0x00328 - 0x00367: Matrix memory mapping table
|
||||
* 0x00368 - 0x0036F: Reserved (leave zero)
|
||||
* 0x00328 - 0x0036F: Special cartridge state, one of:
|
||||
* | Matrix Memory:
|
||||
* | 0x00328 - 0x00367: Matrix memory mapping table
|
||||
* | 0x00368 - 0x0036F: Reserved (leave zero)
|
||||
* | Unlicensed multicart:
|
||||
* | 0x00328: Bank value
|
||||
* | 0x00329: Offset value
|
||||
* | 0x0032A: Size value
|
||||
* | 0x0032B: SRAM active value
|
||||
* | 0x0032C: Unknown value
|
||||
* | 0x0032D: Current size
|
||||
* | 0x0032E - 0x0032F: Current bank/offset
|
||||
* | 0x00330 - 0x00333: Next settle event
|
||||
* | 0x00334 - 0x00337: Flags
|
||||
* | bit 0: Is settling occuring?
|
||||
* | bits 1 - 31: Reserved
|
||||
* | 0x00338 - 0x0036F: Reserved (leave zero)
|
||||
* 0x00370 - 0x0037F: Audio FIFO A samples
|
||||
* 0x00380 - 0x0038F: Audio FIFO B samples
|
||||
* 0x00390 - 0x003CF: Audio rendered samples
|
||||
|
@ -269,9 +287,14 @@ DECL_BITS(GBASerializedHWFlags1, LightCounter, 4, 12);
|
|||
DECL_BITFIELD(GBASerializedHWFlags2, uint8_t);
|
||||
DECL_BITS(GBASerializedHWFlags2, TiltState, 0, 2);
|
||||
DECL_BITS(GBASerializedHWFlags2, GbpInputsPosted, 2, 2);
|
||||
DECL_BITS(GBASerializedHWFlags2, GbpTxPosition, 4, 5);
|
||||
DECL_BITS(GBASerializedHWFlags2, GbpTxPosition, 4, 4);
|
||||
|
||||
DECL_BITFIELD(GBASerializedHWFlags3, uint16_t);
|
||||
DECL_BITFIELD(GBASerializedUnlCartFlags, uint16_t);
|
||||
DECL_BITS(GBASerializedUnlCartFlags, Type, 0, 5);
|
||||
DECL_BITS(GBASerializedUnlCartFlags, Subtype, 5, 3);
|
||||
|
||||
DECL_BITFIELD(GBASerializedMulticartFlags, uint32_t);
|
||||
DECL_BIT(GBASerializedMulticartFlags, DustSettling, 0);
|
||||
|
||||
DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t);
|
||||
DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2);
|
||||
|
@ -370,7 +393,7 @@ struct GBASerializedState {
|
|||
GBASerializedHWFlags1 flags1;
|
||||
uint8_t lightSample;
|
||||
GBASerializedHWFlags2 flags2;
|
||||
GBASerializedHWFlags3 flags3;
|
||||
GBASerializedUnlCartFlags unlCartFlags;
|
||||
uint32_t sioNextEvent;
|
||||
} hw;
|
||||
|
||||
|
@ -407,8 +430,23 @@ struct GBASerializedState {
|
|||
uint32_t nextIrq;
|
||||
int32_t biosStall;
|
||||
|
||||
uint32_t matrixMappings[16];
|
||||
uint32_t reservedMatrix[2];
|
||||
union {
|
||||
struct {
|
||||
uint32_t mappings[16];
|
||||
uint32_t reserved[2];
|
||||
} matrix2;
|
||||
struct {
|
||||
uint8_t bank;
|
||||
uint8_t offset;
|
||||
uint8_t size;
|
||||
uint8_t sramActive;
|
||||
uint8_t unk;
|
||||
uint8_t currentSize;
|
||||
uint16_t currentOffset;
|
||||
uint32_t settleNextEvent;
|
||||
GBASerializedMulticartFlags flags;
|
||||
} multicart;
|
||||
};
|
||||
|
||||
struct {
|
||||
int8_t chA[16];
|
||||
|
|
|
@ -6,6 +6,7 @@ set(SOURCE_FILES
|
|||
cart/ereader.c
|
||||
cart/gpio.c
|
||||
cart/matrix.c
|
||||
cart/unlicensed.c
|
||||
cart/vfame.c
|
||||
cheats.c
|
||||
cheats/codebreaker.c
|
||||
|
|
|
@ -109,7 +109,7 @@ void GBAMatrixSerialize(const struct GBA* gba, struct GBASerializedState* state)
|
|||
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
STORE_32(gba->memory.matrix.mappings[i], i << 2, state->matrixMappings);
|
||||
STORE_32(gba->memory.matrix.mappings[i], i << 2, state->matrix2.mappings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ void GBAMatrixDeserialize(struct GBA* gba, const struct GBASerializedState* stat
|
|||
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
LOAD_32(gba->memory.matrix.mappings[i], i << 2, state->matrixMappings);
|
||||
LOAD_32(gba->memory.matrix.mappings[i], i << 2, state->matrix2.mappings);
|
||||
gba->memory.matrix.paddr = gba->memory.matrix.mappings[i];
|
||||
gba->memory.matrix.vaddr = i << 9;
|
||||
_remapMatrix(gba);
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/* Copyright (c) 2013-2024 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gba/cart/unlicensed.h>
|
||||
|
||||
#include <mgba/internal/arm/macros.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/serialize.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#define MULTI_SETTLE 512
|
||||
#define MULTI_BLOCK 0x80000
|
||||
#define MULTI_BANK 0x2000000
|
||||
|
||||
enum GBMulticartCfgOffset {
|
||||
GBA_MULTICART_CFG_BANK = 0x2,
|
||||
GBA_MULTICART_CFG_OFFSET = 0x3,
|
||||
GBA_MULTICART_CFG_SIZE = 0x4,
|
||||
GBA_MULTICART_CFG_SRAM = 0x5,
|
||||
GBA_MULTICART_CFG_UNK = 0x6,
|
||||
};
|
||||
|
||||
static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||
|
||||
void GBAUnlCartInit(struct GBA* gba) {
|
||||
memset(&gba->memory.unl, 0, sizeof(gba->memory.unl));
|
||||
}
|
||||
|
||||
void GBAUnlCartDetect(struct GBA* gba) {
|
||||
if (!gba->memory.rom) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GBACartridge* cart = (struct GBACartridge*) gba->memory.rom;
|
||||
if (GBAVFameDetect(&gba->memory.unl.vfame, gba->memory.rom, gba->memory.romSize, gba->romCrc32)) {
|
||||
gba->memory.unl.type = GBA_UNL_CART_VFAME;
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp(&cart->id, "AXVJ01", 6) == 0) {
|
||||
if (gba->romVf && gba->romVf->size(gba->romVf) >= 0x04000000) {
|
||||
// Bootleg multicart
|
||||
// TODO: Identify a bit more precisely
|
||||
gba->isPristine = false;
|
||||
GBASavedataInitSRAM(&gba->memory.savedata);
|
||||
gba->memory.unl.type = GBA_UNL_CART_MULTICART;
|
||||
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
|
||||
gba->memory.unl.multi.fullSize = gba->romVf->size(gba->romVf);
|
||||
gba->memory.unl.multi.rom = gba->romVf->map(gba->romVf, gba->memory.unl.multi.fullSize, MAP_READ);
|
||||
gba->memory.rom = gba->memory.unl.multi.rom;
|
||||
gba->memory.hw.gpioBase = NULL;
|
||||
|
||||
gba->memory.unl.multi.settle.context = gba;
|
||||
gba->memory.unl.multi.settle.callback = _multicartSettle;
|
||||
gba->memory.unl.multi.settle.name = "GBA Unlicensed Multicart Settle";
|
||||
gba->memory.unl.multi.settle.priority = 0x71;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GBAUnlCartReset(struct GBA* gba) {
|
||||
if (gba->memory.unl.type == GBA_UNL_CART_MULTICART) {
|
||||
gba->memory.unl.multi.bank = 0;
|
||||
gba->memory.unl.multi.offset = 0;
|
||||
gba->memory.unl.multi.size = 0;
|
||||
gba->memory.rom = gba->memory.unl.multi.rom;
|
||||
gba->memory.romSize = GBA_SIZE_ROM0;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAUnlCartUnload(struct GBA* gba) {
|
||||
if (gba->memory.unl.type == GBA_UNL_CART_MULTICART && gba->romVf) {
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.unl.multi.rom, gba->memory.unl.multi.size);
|
||||
gba->memory.unl.multi.rom = NULL;
|
||||
gba->memory.rom = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAUnlCartWriteSRAM(struct GBA* gba, uint32_t address, uint8_t value) {
|
||||
struct GBAUnlCart* unl = &gba->memory.unl;
|
||||
|
||||
switch (unl->type) {
|
||||
case GBA_UNL_CART_VFAME:
|
||||
GBAVFameSramWrite(&unl->vfame, address, value, gba->memory.savedata.data);
|
||||
return;
|
||||
case GBA_UNL_CART_MULTICART:
|
||||
mLOG(GBA_MEM, DEBUG, "Multicart writing SRAM %06X:%02X", address, value);
|
||||
switch (address) {
|
||||
case GBA_MULTICART_CFG_BANK:
|
||||
unl->multi.bank = value >> 4;
|
||||
mTimingDeschedule(&gba->timing, &unl->multi.settle);
|
||||
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
|
||||
break;
|
||||
case GBA_MULTICART_CFG_OFFSET:
|
||||
unl->multi.offset = value & 0x3F;
|
||||
mTimingDeschedule(&gba->timing, &unl->multi.settle);
|
||||
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
|
||||
break;
|
||||
case GBA_MULTICART_CFG_SIZE:
|
||||
unl->multi.size = 0x40 - (value & 0x3F);
|
||||
mTimingDeschedule(&gba->timing, &unl->multi.settle);
|
||||
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
|
||||
break;
|
||||
case GBA_MULTICART_CFG_SRAM:
|
||||
if (value == 0 && unl->multi.sramActive) {
|
||||
unl->multi.sramActive = false;
|
||||
} else if (value == 1 && !unl->multi.sramActive) {
|
||||
unl->multi.sramActive = true;
|
||||
}
|
||||
break;
|
||||
case GBA_MULTICART_CFG_UNK:
|
||||
// TODO: What does this do?
|
||||
unl->multi.unk = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GBA_UNL_CART_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
gba->memory.savedata.data[address & (GBA_SIZE_SRAM - 1)] = value;
|
||||
}
|
||||
|
||||
static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
struct GBA* gba = context;
|
||||
mLOG(GBA_MEM, INFO, "Switching to bank %i offset %i, size %i",
|
||||
gba->memory.unl.multi.bank, gba->memory.unl.multi.offset, gba->memory.unl.multi.size);
|
||||
size_t offset = gba->memory.unl.multi.bank * (MULTI_BANK >> 2) + gba->memory.unl.multi.offset * (MULTI_BLOCK >> 2);
|
||||
size_t size = gba->memory.unl.multi.size * MULTI_BLOCK;
|
||||
if (offset * 4 >= gba->memory.unl.multi.fullSize || offset * 4 + size > gba->memory.unl.multi.fullSize) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bank switch was out of bounds, %07" PRIz "X + %" PRIz "X > %07" PRIz "X",
|
||||
offset * 4, size, gba->memory.unl.multi.fullSize);
|
||||
return;
|
||||
}
|
||||
gba->memory.rom = gba->memory.unl.multi.rom + offset;
|
||||
gba->memory.romSize = size;
|
||||
}
|
||||
|
||||
void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state) {
|
||||
GBASerializedUnlCartFlags flags = 0;
|
||||
const struct GBAUnlCart* unl = &gba->memory.unl;
|
||||
switch (unl->type) {
|
||||
case GBA_UNL_CART_NONE:
|
||||
return;
|
||||
case GBA_UNL_CART_VFAME:
|
||||
flags = GBASerializedUnlCartFlagsSetType(flags, GBA_UNL_CART_VFAME);
|
||||
flags = GBASerializedUnlCartFlagsSetSubtype(flags, unl->vfame.cartType);
|
||||
mLOG(GBA_MEM, STUB, "Vast Fame save states are not yet implemented");
|
||||
break;
|
||||
case GBA_UNL_CART_MULTICART:
|
||||
flags = GBASerializedUnlCartFlagsSetType(0, GBA_UNL_CART_MULTICART);
|
||||
state->multicart.bank = unl->multi.bank;
|
||||
state->multicart.offset = unl->multi.offset;
|
||||
state->multicart.size = unl->multi.size;
|
||||
state->multicart.sramActive = unl->multi.sramActive;
|
||||
state->multicart.unk = unl->multi.unk;
|
||||
state->multicart.currentSize = gba->memory.romSize / MULTI_BLOCK;
|
||||
STORE_16((gba->memory.rom - unl->multi.rom) / 0x20000, 0, &state->multicart.currentOffset);
|
||||
STORE_32(unl->multi.settle.when, 0, &state->multicart.settleNextEvent);
|
||||
if (mTimingIsScheduled(&gba->timing, &unl->multi.settle)) {
|
||||
STORE_32(GBASerializedMulticartFlagsFillDustSettling(0), 0, &state->multicart.flags);
|
||||
}
|
||||
break;
|
||||
}
|
||||
STORE_32(flags, 0, &state->hw.unlCartFlags);
|
||||
}
|
||||
|
||||
void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||
GBASerializedUnlCartFlags flags;
|
||||
struct GBAUnlCart* unl = &gba->memory.unl;
|
||||
|
||||
LOAD_32(flags, 0, &state->hw.unlCartFlags);
|
||||
enum GBAUnlCartType type = GBASerializedUnlCartFlagsGetType(flags);
|
||||
if (type != unl->type) {
|
||||
mLOG(GBA_STATE, WARN, "Save state expects different bootleg type; not restoring bootleg state");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t when;
|
||||
uint32_t offset;
|
||||
size_t size;
|
||||
GBASerializedMulticartFlags multiFlags;
|
||||
|
||||
switch (type) {
|
||||
case GBA_UNL_CART_NONE:
|
||||
return;
|
||||
case GBA_UNL_CART_VFAME:
|
||||
mLOG(GBA_MEM, STUB, "Vast Fame save states are not yet implemented");
|
||||
return;
|
||||
case GBA_UNL_CART_MULTICART:
|
||||
unl->multi.bank = state->multicart.bank;
|
||||
unl->multi.offset = state->multicart.offset;
|
||||
unl->multi.size = state->multicart.size;
|
||||
unl->multi.sramActive = state->multicart.sramActive;
|
||||
unl->multi.unk = state->multicart.unk;
|
||||
size = state->multicart.currentSize * MULTI_BLOCK;
|
||||
LOAD_16(offset, 0, &state->multicart.currentOffset);
|
||||
offset *= 0x20000;
|
||||
if (offset * 4 >= gba->memory.unl.multi.fullSize || offset * 4 + size > gba->memory.unl.multi.fullSize) {
|
||||
mLOG(GBA_STATE, WARN, "Multicart save state has corrupted ROM offset");
|
||||
} else {
|
||||
gba->memory.romSize = size;
|
||||
gba->memory.rom = unl->multi.rom + offset;
|
||||
}
|
||||
LOAD_32(multiFlags, 0, &state->multicart.flags);
|
||||
if (GBASerializedMulticartFlagsIsDustSettling(multiFlags)) {
|
||||
LOAD_32(when, 0, &state->multicart.settleNextEvent);
|
||||
mTimingSchedule(&gba->timing, &unl->multi.settle, when);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gba/cart/vfame.h>
|
||||
#include <mgba/internal/gba/cart/unlicensed.h>
|
||||
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/memory.h>
|
||||
|
@ -60,25 +60,19 @@ static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mo
|
|||
static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, int mode);
|
||||
static int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength);
|
||||
|
||||
void GBAVFameInit(struct GBAVFameCart* cart) {
|
||||
cart->cartType = VFAME_NO;
|
||||
cart->sramMode = -1;
|
||||
cart->romMode = -1;
|
||||
cart->acceptingModeChange = false;
|
||||
}
|
||||
|
||||
void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32) {
|
||||
cart->cartType = VFAME_NO;
|
||||
|
||||
bool GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32) {
|
||||
// The initialisation code is also present & run in the dumps of Digimon Ruby & Sapphire from hacked/deprotected reprint carts,
|
||||
// which would break if run in "proper" VFame mode so we need to exclude those..
|
||||
if (romSize == 0x2000000) { // the deprotected dumps are 32MB but no real VF games are this size
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool detected = false;
|
||||
|
||||
// Most games have the same init sequence in the same place
|
||||
// but LOTR/Mo Jie Qi Bing doesn't, probably because it's based on the Kiki KaiKai engine, so just detect based on its title
|
||||
if (memcmp(INIT_SEQUENCE, &rom[0x57], sizeof(INIT_SEQUENCE)) == 0 || memcmp("\0LORD\0WORD\0\0AKIJ", &((struct GBACartridge*) rom)->title, 16) == 0) {
|
||||
detected = true;
|
||||
cart->cartType = VFAME_STANDARD;
|
||||
mLOG(GBA_MEM, INFO, "Vast Fame game detected");
|
||||
}
|
||||
|
@ -87,13 +81,23 @@ void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, ui
|
|||
// Their initialisation seems to be identical so the difference must be in the cart HW itself
|
||||
// Other undumped games may have similar differences
|
||||
if (memcmp("George Sango", &((struct GBACartridge*) rom)->title, 12) == 0) {
|
||||
detected = true;
|
||||
cart->cartType = VFAME_GEORGE;
|
||||
mLOG(GBA_MEM, INFO, "George mode");
|
||||
} else if (crc32 == DIGIMON_SAPPHIRE_CHINESE_CRC32) {
|
||||
// Chinese version of Digimon Sapphire; header is identical to the English version which uses the normal reordering
|
||||
// so we have to use some other way to detect it
|
||||
detected = true;
|
||||
cart->cartType = VFAME_ALTERNATE;
|
||||
}
|
||||
|
||||
if (detected) {
|
||||
cart->sramMode = -1;
|
||||
cart->romMode = -1;
|
||||
cart->acceptingModeChange = false;
|
||||
}
|
||||
|
||||
return detected;
|
||||
}
|
||||
|
||||
// This is not currently being used but would be called on ROM reads
|
||||
|
@ -235,7 +239,6 @@ static uint32_t _patternRightShift2(uint32_t addr) {
|
|||
}
|
||||
|
||||
void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData) {
|
||||
address &= 0x00FFFFFF;
|
||||
// A certain sequence of writes to SRAM FFF8->FFFC can enable or disable "mode change" mode
|
||||
// Currently unknown if these writes have to be sequential, or what happens if you write different values, if anything
|
||||
if (address >= 0xFFF8 && address <= 0xFFFC) {
|
||||
|
|
|
@ -140,6 +140,9 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
|
||||
void GBAUnloadROM(struct GBA* gba) {
|
||||
GBAMemoryClearAGBPrint(gba);
|
||||
if (gba->memory.unl.type) {
|
||||
GBAUnlCartUnload(gba);
|
||||
}
|
||||
if (gba->memory.rom && !gba->isPristine) {
|
||||
if (gba->yankedRomSize) {
|
||||
gba->yankedRomSize = 0;
|
||||
|
@ -492,7 +495,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
|
|||
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
|
||||
}
|
||||
GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
|
||||
GBAVFameDetect(&gba->memory.vfame, gba->memory.rom, gba->memory.romSize, gba->romCrc32);
|
||||
GBAUnlCartDetect(gba);
|
||||
// TODO: error check
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1033,6 +1033,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
STORE_32(gba->bus, 0, &state->bus);
|
||||
|
||||
GBAHardwareSerialize(&gba->memory.hw, state);
|
||||
GBAUnlCartSerialize(gba, state);
|
||||
}
|
||||
|
||||
void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||
|
@ -1082,4 +1083,5 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
GBADMARecalculateCycles(gba);
|
||||
GBADMAUpdate(gba);
|
||||
GBAHardwareDeserialize(&gba->memory.hw, state);
|
||||
GBAUnlCartDeserialize(gba, state);
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ void GBAMemoryInit(struct GBA* gba) {
|
|||
gba->memory.iwram = &gba->memory.wram[GBA_SIZE_EWRAM >> 2];
|
||||
|
||||
GBADMAInit(gba);
|
||||
GBAVFameInit(&gba->memory.vfame);
|
||||
GBAUnlCartInit(gba);
|
||||
|
||||
gba->memory.ereader.p = gba;
|
||||
gba->memory.ereader.dots = NULL;
|
||||
|
@ -139,6 +139,7 @@ void GBAMemoryReset(struct GBA* gba) {
|
|||
}
|
||||
|
||||
GBADMAReset(gba);
|
||||
GBAUnlCartReset(gba);
|
||||
memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix));
|
||||
}
|
||||
|
||||
|
@ -411,7 +412,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
wait += waitstatesRegion[address >> BASE_OFFSET]; \
|
||||
if ((address & (GBA_SIZE_ROM0 - 4)) < memory->romSize) { \
|
||||
LOAD_32(value, address & (GBA_SIZE_ROM0 - 4), memory->rom); \
|
||||
} else if (memory->vfame.cartType) { \
|
||||
} else if (memory->unl.type == GBA_UNL_CART_VFAME) { \
|
||||
value = GBAVFameGetPatternValue(address, 32); \
|
||||
} else { \
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load32: 0x%08X", address); \
|
||||
|
@ -576,7 +577,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||
if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) {
|
||||
LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom);
|
||||
} else if (memory->vfame.cartType) {
|
||||
} else if (memory->unl.type == GBA_UNL_CART_VFAME) {
|
||||
value = GBAVFameGetPatternValue(address, 16);
|
||||
} else if ((address & (GBA_SIZE_ROM0 - 2)) >= AGB_PRINT_BASE) {
|
||||
uint32_t agbPrintAddr = address & 0x00FFFFFF;
|
||||
|
@ -601,7 +602,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
value = GBACartEReaderRead(&memory->ereader, address);
|
||||
} else if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) {
|
||||
LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom);
|
||||
} else if (memory->vfame.cartType) {
|
||||
} else if (memory->unl.type == GBA_UNL_CART_VFAME) {
|
||||
value = GBAVFameGetPatternValue(address, 16);
|
||||
} else {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address);
|
||||
|
@ -692,7 +693,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||
if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) {
|
||||
value = ((uint8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)];
|
||||
} else if (memory->vfame.cartType) {
|
||||
} else if (memory->unl.type == GBA_UNL_CART_VFAME) {
|
||||
value = GBAVFameGetPatternValue(address, 8);
|
||||
} else {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load8: 0x%08X", address);
|
||||
|
@ -1056,8 +1057,8 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
|||
} else if (memory->savedata.type == GBA_SAVEDATA_FLASH512 || memory->savedata.type == GBA_SAVEDATA_FLASH1M) {
|
||||
GBASavedataWriteFlash(&memory->savedata, address, value);
|
||||
} else if (memory->savedata.type == GBA_SAVEDATA_SRAM) {
|
||||
if (memory->vfame.cartType) {
|
||||
GBAVFameSramWrite(&memory->vfame, address, value, memory->savedata.data);
|
||||
if (memory->unl.type) {
|
||||
GBAUnlCartWriteSRAM(gba, address & 0xFFFF, value);
|
||||
} else {
|
||||
memory->savedata.data[address & (GBA_SIZE_SRAM - 1)] = value;
|
||||
}
|
||||
|
|
|
@ -377,6 +377,12 @@ void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overr
|
|||
struct GBACartridgeOverride override = { .idleLoop = GBA_IDLE_LOOP_NONE };
|
||||
const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
|
||||
if (cart) {
|
||||
if (gba->memory.unl.type == GBA_UNL_CART_MULTICART) {
|
||||
override.savetype = GBA_SAVEDATA_SRAM;
|
||||
GBAOverrideApply(gba, &override);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(override.id, &cart->id, sizeof(override.id));
|
||||
|
||||
static const uint32_t pokemonTable[] = {
|
||||
|
|
|
@ -32,8 +32,17 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
STORE_64LE(gba->timing.globalCycles, 0, &state->globalCycles);
|
||||
|
||||
if (gba->memory.rom) {
|
||||
state->id = ((struct GBACartridge*) gba->memory.rom)->id;
|
||||
memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title));
|
||||
switch (gba->memory.unl.type) {
|
||||
case GBA_UNL_CART_NONE:
|
||||
case GBA_UNL_CART_VFAME:
|
||||
state->id = ((struct GBACartridge*) gba->memory.rom)->id;
|
||||
memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title));
|
||||
break;
|
||||
case GBA_UNL_CART_MULTICART:
|
||||
state->id = ((struct GBACartridge*) gba->memory.unl.multi.rom)->id;
|
||||
memcpy(state->title, ((struct GBACartridge*) gba->memory.unl.multi.rom)->title, sizeof(state->title));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
state->id = 0;
|
||||
memset(state->title, 0, sizeof(state->title));
|
||||
|
@ -106,9 +115,21 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
error = true;
|
||||
}
|
||||
}
|
||||
if (gba->memory.rom && (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)))) {
|
||||
mLOG(GBA_STATE, WARN, "Savestate is for a different game");
|
||||
error = true;
|
||||
if (gba->memory.rom) {
|
||||
struct GBACartridge* cart;
|
||||
switch (gba->memory.unl.type) {
|
||||
case GBA_UNL_CART_NONE:
|
||||
case GBA_UNL_CART_VFAME:
|
||||
cart = (struct GBACartridge*) gba->memory.rom;
|
||||
break;
|
||||
case GBA_UNL_CART_MULTICART:
|
||||
cart = (struct GBACartridge*) gba->memory.unl.multi.rom;
|
||||
break;
|
||||
}
|
||||
if (state->id != cart->id || memcmp(state->title, cart->title, sizeof(state->title))) {
|
||||
mLOG(GBA_STATE, WARN, "Savestate is for a different game");
|
||||
error = true;
|
||||
}
|
||||
} else if (!gba->memory.rom && state->id != 0) {
|
||||
mLOG(GBA_STATE, WARN, "Savestate is for a game, but no game loaded");
|
||||
error = true;
|
||||
|
|
Loading…
Reference in New Issue