Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2019-06-28 17:11:20 -07:00
commit 342ace070a
19 changed files with 1469 additions and 75 deletions

View File

@ -21,6 +21,7 @@ Misc:
0.8.0: (Future)
Features:
- Improved logging configuration
- One-Player BattleChip/Progress/Beast Link Gate support
Bugfixes:
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
- GBA: Reset now reloads multiboot ROMs
@ -29,6 +30,11 @@ Bugfixes:
- Qt: Fix tile and sprite views not always displaying at first
- GBA Memory: Fix a few AGBPrint crashes
- GBA Memory: Fix OOB ROM reads showing up as AGBPrint memory
- GB Serialize: Fix loading states with negative pixel x (fixes mgba.io/i/1293)
- Qt: Fix audio context holding onto closed game controller
- Switch: Fix gyroscope orientation (fixes mgba.io/i/1300)
- GBA SIO: Prevent writing read-only multiplayer bits
- Qt: Fix color picking in sprite view (fixes mgba.io/i/1307)
Misc:
- GBA Savedata: EEPROM performance fixes
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash

View File

@ -60,11 +60,20 @@ struct GBASIODriver {
void GBASIOJOYCreate(struct GBASIODriver* sio);
int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data);
enum GBASIOBattleChipGateFlavor {
GBA_FLAVOR_BATTLECHIP_GATE = 4,
GBA_FLAVOR_PROGRESS_GATE = 5,
GBA_FLAVOR_BEAST_LINK_GATE = 6,
GBA_FLAVOR_BEAST_LINK_GATE_US = 7,
};
struct GBASIOBattlechipGate {
struct GBASIODriver d;
struct mTimingEvent event;
uint16_t chipId;
int32_t index;
uint16_t data[2];
int state;
int flavor;
};
void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate*);

315
res/chip-names-4.txt Normal file
View File

@ -0,0 +1,315 @@
Cannon
HiCannon
MegaCannon
AirShot
Blizzard
HeatBreath
Silence
Tornado
WideShot1
WideShot2
WideShot3
FlameLine1
FlameLine2
FlameLine3
Vulcan1
Vulcan2
Vulcan3
Spreader
HeatShot
HeatV
HeatSide
Bubbler
BubbleV
BubbleSide
ElementFlare
ElementIce
Static
LifeSync
MiniBomb
EnergyBomb
MegaEnergyBomb
GunDelSol1
GunDelSol2
GunDelSol3
MagBolt1
MagBolt2
MagBolt3
Binder1
Binder2
Binder3
BugBomb
ElecShock
WoodPowder
CannonBall
Geyser
BlackBomb
SandRing
Sword
WideSword
LongSword
WideBlade
LongBlade
WindRacket
CustomSword
VariableSword
Slasher
ThunderBall1
ThunderBall2
ThunderBall3
Counter1
Counter2
Counter3
AirHockey1
AirHockey2
AirHockey3
CircleGun1
CircleGun2
CircleGun3
TwinFang1
TwinFang2
TwinFang3
WhiteWeb1
WhiteWeb2
WhiteWeb3
Boomerang1
Boomerang2
Boomerang3
SideBamboo1
SideBamboo2
SideBamboo3
Lance
Hole
Boy'sBomb1
Boy'sBomb2
Boy'sBomb3
Guard1
Guard2
Guard3
Magnum
MetaGel
Snake
TimeBomb
Mine
RockCube
Fanfare
Discord
Timpani
Vdoll
BigHammer1
BigHammer2
BigHammer3
GrabRevenge
GrabBanish
Geddon1
Geddon2
Geddon3
ElementLeaf
ColorPoint
ElementSand
MokoRush1
MokoRush2
MokoRush3
NorthWind
AntiFire
AntiWater
AntiElectric
AntiWood
AntiNavi
AntiDamage
AntiSword
AntiRecover
CopyDamage
Attack+10
Navi+20
RollArrow1
RollArrow2
RollArrow3
GutsPunch1
GutsPunch2
GutsPunch3
PropellerBomb1
PropellerBomb2
PropellerBomb3
SearchBomb1
SearchBomb2
SearchBomb3
Meteors1
Meteors2
Meteors3
Lightning1
Lightning2
Lightning3
HawkCut1
HawkCut2
HawkCut3
NumberBall1
NumberBall2
NumberBall3
MetalGear1
MetalGear2
MetalGear3
PanelShoot1
PanelShoot2
PanelShoot3
AquaUpper1
AquaUpper2
AquaUpper3
GreenWood1
GreenWood2
GreenWood3
Muramasa
Guardian
Anubis
DoublePoint
FullCustom
ShootingStar
BugChain
Jealousy
ElementDark
BlackWing
GodHammer
DarkLine
NeoVariable
ZSaber
GunDelSolEX
SuperVulcan
Roll
RollSP
RollDS
GutsMan
GutsManSP
GutsManDS
WindMan
WindManSP
WindManDS
SearchMan
SearchManSP
SearchManDS
FireMan
FireManSP
FireManDS
ThunderMan
ThunderManSP
ThunderManDS
ProtoMan
ProtoManSP
ProtoManDS
NumberMan
NumberManSP
NumberManDS
MetalMan
MetalManSP
MetalManDS
JunkMan
JunkManSP
JunkManDS
AquaMan
AquaManSP
AquaManDS
WoodMan
WoodManSP
WoodManDS
TopMan
TopManSP
TopManDS
ShadeMan
ShadeManSP
ShadeManDS
BurnerMan
BurnerManSP
BurnerManDS
ColdMan
ColdManSP
ColdManDS
SparkMan
SparkManSP
SparkManDS
LaserMan
LaserManSP
LaserManDS
KendoMan
KendoManSP
KendoManDS
VideoMan
VideoManSP
VideoManDS
Marking
CannonMode
CannonballMode
SwordMode
FirePlus
ThunderPlus
AquaPower
WoodPower
BlackWeapon
FinalGun
Bass
DeltaRay
BugCurse
RedSun
BassAnomaly
HolyDream
BlueMoon
BugCharge
Wind
Fan
CrackOut
DoubleCrack
TripleCrack
Recover10
Recover30
Recover50
Recover80
Recover120
Recover150
Recover200
Recover300
Repair
PanelGrab
AreaGrab
SlowGauge
FastGauge
PanelReturn
Blinder
PopUp
Invisible
Barrier
Barrier100
Barrier200
HolyPanel
LifeAura
Attack+30
BugFix
Sanctuary
SignalRed
BlackBarrier
MegaManNavi
RollNavi
GutsManNavi
WindManNavi
SearchManNavi
FireManNavi
ThunderManNavi
ProtoManNavi
NumberManNavi
MetalManNavi
JunkManNavi
AquaManNavi
WoodManNavi
StarManNavi
IceManNavi
ShadowManNavi
ElecManNavi
KnightManNavi
PlantManNavi
NapalmManNavi
BassNavi

351
res/chip-names-5.txt Normal file
View File

@ -0,0 +1,351 @@
Cannon
HiCannon
MegaCannon
AirShot
AirHockey
Boomer
Silence
Tornado
WideShot1
WideShot2
WideShot3
MarkCannon1
MarkCannon2
MarkCannon3
Vulcan1
Vulcan2
Vulcan3
Spreader
ThunderBall
IceSeed
Pulsar1
Pulsar2
Pulsar3
SpaceShake1
SpaceShake2
SpaceShake3
Static
LifeSync
MiniBomb
EnergyBomb
MegaEnergyBomb
GunDelSol1
GunDelSol2
GunDelSol3
Quake1
Quake2
Quake3
CrackBomb
ParalyzeBomb
ResetBomb
BugBomb
GrassSeed
LavaSeed
CannonBall
Geyser
BlackBomb
SeaSeed
Sword
WideSword
LongSword
WideBlade
LongBlade
WindRacket
CustomSword
VariableSword
Slasher
MoonBlade1
MoonBlade2
MoonBlade3
Katana1
Katana2
Katana3
TankCannon1
TankCannon2
TankCannon3
RedFruit1
RedFruit2
RedFruit3
Skully1
Skully2
Skully3
DrillArm1
DrillArm2
DrillArm3
TimeBomb1
TimeBomb2
TimeBomb3
Voltz1
Voltz2
Voltz3
Lance
Yo-Yo
Wind
Fan
Boy'sBomb1
Boy'sBomb2
Boy'sBomb3
Guard1
Guard2
Guard3
CrackOut
DoubleCrack
TripleCrack
Magnum
MetaGel
Snake
CircleGun
Mine
RockCube
Fanfare
Discord
Timpani
Vdoll
Asteroid1
Asteroid2
Asteroid3
Recover10
Recover30
Recover50
Recover80
Recover120
Recover150
Recover200
Recover300
BusterUp
PanelGrab
AreaGrab
GrabRevenge
GrabBanish
SlowGauge
FastGauge
PanelReturn
Geddon1
Geddon2
Geddon3
RainyDay
ColorPoint
ElementRage
Blinder
AirSpin1
AirSpin2
AirSpin3
Invisible
BubbleWrap
Barrier
Barrier100
Barrier200
NorthWind
HolyPanel
AntiFire
AntiWater
AntiElectric
AntiWood
AntiNavi
AntiDamage
AntiSword
AntiRecovery
CopyDamage
Attack+10
Navi+20
FireHit1
FireHit2
FireHit3
HotBody1
HotBody2
HotBody3
AquaWhirl1
AquaWhirl2
AquaWhirl3
SideBubble1
SideBubble2
SideBubble3
ElecReel1
ElecReel2
ElecReel3
CustomVolt1
CustomVolt2
CustomVolt3
CurseShield1
CurseShield2
CurseShield3
WavePit
RedWave
MudWave
CactusBall1
CactusBall2
CactusBall3
WoodyNose1
WoodyNose2
WoodyNose3
DarkCircle
DarkSword
DarkInvis
DarkPlus
DarkLance
DarkWide
DarkThunder
DarkRecovery
DarkMeteor
DarkDrill
DarkTornado
DarkSonic
LifeAura
Muramasa
Guardian
Anubis
Attack+30
BugFix
DoublePoint
Sanctuary
FullCustom
Meteors
NumberBall
Jealousy
Poltergeist
BlackWing
Otenko
JusticeOne
NeoVariable
ZSaber
GunDelSolEX
SuperVulcan
Roll
RollSP
RollDS
GyroMan
GyroManSP
GyroManDS
NapalmMan
NapalmManSP
NapalmManDS
SearchMan
SearchManSP
SearchManDS
MagnetMan
MagnetManSP
MagnetManDS
Meddy
MeddyDS
MeddySP
ProtoMan
ProtoManSP
ProtoManDS
NumberMan
NumberManSP
NumberManDS
Colonel
ColonelSP
ColonelDS
ShadowMan
ShadowManSP
ShadowManDS
TomahawkMan
TomahawkManSP
TomahawkManDS
KnightMan
KnightManSP
KnightManDS
ToadMan
ToadManSP
ToadManDS
ShadeMan
ShadeManSP
ShadeManDS
BlizzardMan
BlizzardManSP
BlizzardManDS
CloudMan
CloudManSP
CloudManDS
CosmoMan
CosmoManSP
CosmoManDS
LarkMan
LarkManSP
LarkManDS
GridMan
GridManSP
GridManDS
Django
DjangoSP
DjangoDS
CannonMode
CannonBall
SwordMode
Yo-YoMode
DrillMode
LCurseShield
LStepSword
LCounter
ElementPower
FinalGun
Bass
DeltaRay
BugCurse
MeteorKnuckle
OmegaRocket
BassAnomaly
HolyDream
BigHook
CrossDivide
BugCharge
MegaManNavi
SearchManNavi
ProtoManNavi
NumberManNavi
ShadowManNavi
NapalmManNavi
KnightManNavi
ToadManNavi
MagnetManNavi
GyroManNavi
ColonelNavi
MeddyNavi
TomahawkManNavi

500
res/chip-names-6.txt Normal file
View File

@ -0,0 +1,500 @@
Cannon
HiCannon
MegaCannon
AirShot
Vulcan1
Vulcan2
Vulcan3
SuperVulcan
Spreader1
Spreader2
Spreader3
TankCannon1
TankCannon2
TankCannon3
GunDelSol1
GunDelSol2
GunDelSol3
GunDelSolEX
Yo-Yo
FireBurner1
FireBurner2
FireBurner3
WideShot
TrainArrow1
TrainArrow2
TrainArrow3
BubbleStar1
BubbleStar2
BubbleStar3
Thunder
DollThunder1
DollThunder2
DollThunder3
ElecPulse1
ElecPulse2
ElecPulse3
RiskyHoney1
RiskyHoney2
RiskyHoney3
RollingLog1
RollingLog2
RollingLog3
MachineGun1
MachineGun2
MachineGun3
HeatDragon
ElecDragon
AquaDragon
WoodDragon
AirHockey
DrillArm
Tornado
Static
MiniBomb
EnergyBomb
MegaEnergyBomb
FlashBomb1
FlashBomb2
FlashBomb3
BlackBomb
AquaNeedle1
AquaNeedle2
AquaNeedle3
CornShot1
CornShot2
CornShot3
BugBomb
GrassSeed
IceSeed
PoisonSeed
Sword
WideSword
LongSword
WideBlade
LongBlade
FireSword
AquaSword
ElecSword
BambooSword
WindRacket
StepSword
VariableSword
NeoVariable
MoonBlade
Muramasa
MachineSword
ElementSword
AssassinSword
CrackShot
DoubleShot
TripleShot
WaveArm1
WaveArm2
WaveArm3
AuraHead1
AuraHead2
AuraHead3
LittleBoiler1
LittleBoiler2
LittleBoiler3
SandWorm1
SandWorm2
SandWorm3
AirRaid1
AirRaid2
AirRaid3
FireHit1
FireHit2
FireHit3
BurnSquare1
BurnSquare2
BurnSquare3
Sensor1
Sensor2
Sensor3
Boomer
HiBoomer
MegaBoomer
Lance
GolemHit1
GolemHit2
GolemHit3
IronShell1
IronShell2
IronShell3
AirSpin1
AirSpin2
AirSpin3
Wind
Fan
Reflector1
Reflector2
Reflector3
Snake
SummonBlack1
SummonBlack2
SummonBlack3
NumberBall
Meteors
JusticeOne
Magnum
CircleGun
RockCube
TimeBomb1
Mine
Fanfare
Discord
Timpani
Silence
Vdoll
Guardian
Anubis
Otenko
Recover10
Recover30
Recover50
Recover80
Recover120
Recover150
Recover200
Recover300
PanelGrab
AreaGrab
GrabBanish
GrabRevenge
PanelReturn
Geddon
HolyPanel
Sanctuary
ComingRoad
GoingRoad
SlowGauge
FastGauge
FullCustom
BusterUp
BugFix
Invisible
Barrier
Barrier100
Barrier200
BubbleWrap
LifeAura
MagnetCoil
WhiteCapsule
Uninstall
AntiNavi
AntiDamage
AntiSword
AntiRecover
CopyDamage
LifeSync
Attack+10
Navi+20
ColorPoint
Attack+30
DoublePoint
ElementTrap
ColonelArmy
BlizzardBall
TimeBomb2
TimeBomb3
BigBomb
DarkTornado
DarkCircle
DarkMeteors
DarkLance
DarkWide
Roll
Roll2
Roll3
ProtoMan
ProtoManEX
ProtoManSP
HeatMan
HeatManEX
HeatManSP
ElecMan
ElecManEX
ElecManSP
SlashMan
SlashManEX
SlashManSP
EraseMan
EraseManEX
EraseManSP
ChargeMan
ChargeManEX
ChargeManSP
SpoutMan
SpoutManEX
SpoutManSP
TomahawkMan
TomahawkManEX
TomahawkManSP
TenguMan
TenguManEX
TenguManSP
GroundMan
GroundManEX
GroundManSP
DustMan
DustManEX
DustManSP
BlastMan
BlastManEX
BlastManSP
DiveMan
DiveManEX
DiveManSP
CircusMan
CircusManEX
CircusManSP
JudgeMan
JudgeManEX
JudgeManSP
ElementMan
ElementManEX
ElementManSP
Colonel
ColonelEX
ColonelSP
Count
CountEX
CountSP
Django
Django2
Django3
PunchArm
NeedleArm
PuzzleArm
BoomerArm
SynchroTrigger
DarkSword
DarkThunder
DarkRecover
DarkInvisible
DarkPlus
Bass
BigHook
DeltaRay
ColonelForce
BugRiseSword
BassAnomaly
MeteorKnuckle
CrossDivide
HubBatch
BugDeathThunder
DoubleBeast
Gregar
Falzar
MegaManV1
MegaManV2
MegaManV3
MegaManV4
MegaManV5
MegaManV6
MegaManV7
MegaManV8
MegaManV9
MegaManV10
MegaManV11
MegaManV12
MegaManV13
MegaManV14
MegaManSP
HeatManV1
HeatManV2
HeatManV3
HeatManV4
HeatManV5
HeatManV6
HeatManV7
HeatManV8
HeatManV9
HeatManV10
HeatManV11
HeatManV12
HeatManV13
HeatManV14
HeatManSP
ElecManV1
ElecManV2
ElecManV3
ElecManV4
ElecManV5
ElecManV6
ElecManV7
ElecManV8
ElecManV9
ElecManV10
ElecManV11
ElecManV12
ElecManV13
ElecManV14
ElecManSP
SlashManV1
SlashManV2
SlashManV3
SlashManV4
SlashManV5
SlashManV6
SlashManV7
SlashManV8
SlashManV9
SlashManV10
SlashManV11
SlashManV12
SlashManV13
SlashManV14
SlashManSP
EraseManV1
EraseManV2
EraseManV3
EraseManV4
EraseManV5
EraseManV6
EraseManV7
EraseManV8
EraseManV9
EraseManV10
EraseManV11
EraseManV12
EraseManV13
EraseManV14
EraseManSP
ChargeManV1
ChargeManV2
ChargeManV3
ChargeManV4
ChargeManV5
ChargeManV6
ChargeManV7
ChargeManV8
ChargeManV9
ChargeManV10
ChargeManV11
ChargeManV12
ChargeManV13
ChargeManV14
ChargeManSP
SpoutManV1
SpoutManV2
SpoutManV3
SpoutManV4
SpoutManV5
SpoutManV6
SpoutManV7
SpoutManV8
SpoutManV9
SpoutManV10
SpoutManV11
SpoutManV12
SpoutManV13
SpoutManV14
SpoutManSP
TomahawkManV1
TomahawkManV2
TomahawkManV3
TomahawkManV4
TomahawkManV5
TomahawkManV6
TomahawkManV7
TomahawkManV8
TomahawkManV9
TomahawkManV10
TomahawkManV11
TomahawkManV12
TomahawkManV13
TomahawkManV14
TomahawkManSP
TenguManV1
TenguManV2
TenguManV3
TenguManV4
TenguManV5
TenguManV6
TenguManV7
TenguManV8
TenguManV9
TenguManV10
TenguManV11
TenguManV12
TenguManV13
TenguManV14
TenguManSP
GroundManV1
GroundManV2
GroundManV3
GroundManV4
GroundManV5
GroundManV6
GroundManV7
GroundManV8
GroundManV9
GroundManV10
GroundManV11
GroundManV12
GroundManV13
GroundManV14
GroundManSP
DustManV1
DustManV2
DustManV3
DustManV4
DustManV5
DustManV6
DustManV7
DustManV8
DustManV9
DustManV10
DustManV11
DustManV12
DustManV13
DustManV14
DustManSP
ProtoManV1
ProtoManV2
ProtoManV3
ProtoManV4
ProtoManV5
ProtoManV6
ProtoManV7
ProtoManV8
ProtoManV9
ProtoManV10
ProtoManV11
ProtoManV12
ProtoManV13
ProtoManV14
ProtoManSP

View File

@ -113,7 +113,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
error = true;
}
LOAD_16LE(check16, 0, &state->video.x);
if (check16 < 0 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) {
if (check16 < -7 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: video x is out of range");
error = true;
}

View File

@ -557,6 +557,7 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
break;
case mPERIPH_GBA_BATTLECHIP_GATE:
GBASIOSetDriver(&gba->sio, periph, SIO_MULTI);
GBASIOSetDriver(&gba->sio, periph, SIO_NORMAL_32);
break;
default:
return;

View File

@ -13,18 +13,26 @@ mLOG_DECLARE_CATEGORY(GBA_BATTLECHIP);
mLOG_DEFINE_CATEGORY(GBA_BATTLECHIP, "GBA BattleChip Gate", "gba.battlechip");
enum {
BATTLECHIP_INDEX_HANDSHAKE_0 = 0,
BATTLECHIP_INDEX_HANDSHAKE_1 = 1,
BATTLECHIP_INDEX_ID = 2,
BATTLECHIP_INDEX_END = 6
BATTLECHIP_STATE_SYNC = -1,
BATTLECHIP_STATE_COMMAND = 0,
BATTLECHIP_STATE_UNK_0 = 1,
BATTLECHIP_STATE_UNK_1 = 2,
BATTLECHIP_STATE_DATA_0 = 3,
BATTLECHIP_STATE_DATA_1 = 4,
BATTLECHIP_STATE_ID = 5,
BATTLECHIP_STATE_UNK_2 = 6,
BATTLECHIP_STATE_UNK_3 = 7,
BATTLECHIP_STATE_END = 8
};
enum {
BATTLECHIP_OK = 0xFFC6,
PROGRESS_GATE_OK = 0xFFC7,
BEAST_LINK_GATE_OK = 0xFFC4,
BEAST_LINK_GATE_US_OK = 0xFF00,
BATTLECHIP_CONTINUE = 0xFFFF,
};
static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
@ -32,7 +40,7 @@ static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
gate->d.init = GBASIOBattlechipGateInit;
gate->d.init = NULL;
gate->d.deinit = NULL;
gate->d.load = GBASIOBattlechipGateLoad;
gate->d.unload = NULL;
@ -41,17 +49,16 @@ void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
gate->event.context = gate;
gate->event.callback = _battlechipTransferEvent;
gate->event.priority = 0x80;
}
bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
gate->chipId = 0;
return true;
gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE;
}
bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
gate->index = BATTLECHIP_INDEX_END;
gate->state = BATTLECHIP_STATE_SYNC;
gate->data[0] = 0x00FE;
gate->data[1] = 0xFFFE;
return true;
}
@ -76,13 +83,29 @@ uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t
}
void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
int32_t cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
int32_t cycles;
if (gate->d.p->mode == SIO_NORMAL_32) {
cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000;
} else {
cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
}
mTimingDeschedule(&gate->d.p->p->timing, &gate->event);
mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
}
void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBASIOBattlechipGate* gate = user;
if (gate->d.p->mode == SIO_NORMAL_32) {
gate->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0;
gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
gate->d.p->normalControl.start = 0;
if (gate->d.p->normalControl.irq) {
GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
}
return;
}
uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
uint16_t reply = 0xFFFF;
gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
@ -91,49 +114,81 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
gate->d.p->multiplayerControl.busy = 0;
gate->d.p->multiplayerControl.id = 0;
mLOG(GBA_BATTLECHIP, DEBUG, "> %04x", cmd);
mLOG(GBA_BATTLECHIP, DEBUG, "Game: %04X (%i)", cmd, gate->state);
switch (cmd) {
case 0x4000:
gate->index = 0;
// Fall through
case 0:
switch (gate->index) {
case BATTLECHIP_INDEX_HANDSHAKE_0:
reply = 0x00FE;
break;
case BATTLECHIP_INDEX_HANDSHAKE_1:
reply = 0xFFFE;
break;
case BATTLECHIP_INDEX_ID:
reply = gate->chipId;
break;
default:
if (gate->index >= BATTLECHIP_INDEX_END) {
reply = BATTLECHIP_OK;
} else if (gate->index < 0) {
reply = BATTLECHIP_CONTINUE;
} else {
reply = 0;
}
break;
}
++gate->index;
break;
case 0x8FFF:
gate->index = -2;
// Fall through
uint16_t ok;
switch (gate->flavor) {
case GBA_FLAVOR_BATTLECHIP_GATE:
default:
case 0xA3D0:
reply = BATTLECHIP_OK;
ok = BATTLECHIP_OK;
break;
case 0x4234:
case 0x574A:
reply = BATTLECHIP_CONTINUE;
case GBA_FLAVOR_PROGRESS_GATE:
ok = PROGRESS_GATE_OK;
break;
case GBA_FLAVOR_BEAST_LINK_GATE:
ok = BEAST_LINK_GATE_OK;
break;
case GBA_FLAVOR_BEAST_LINK_GATE_US:
ok = BEAST_LINK_GATE_US_OK;
break;
}
mLOG(GBA_BATTLECHIP, DEBUG, "< %04x", reply);
if (gate->state != BATTLECHIP_STATE_COMMAND) {
// Resync if needed
switch (cmd) {
// EXE 5, 6
case 0xA380:
case 0xA390:
case 0xA3A0:
case 0xA3B0:
case 0xA3C0:
case 0xA3D0:
// EXE 4
case 0xA6C0:
mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected");
gate->state = BATTLECHIP_STATE_SYNC;
break;
}
}
switch (gate->state) {
case BATTLECHIP_STATE_SYNC:
if (cmd != 0x8FFF) {
--gate->state;
}
// Fall through
case BATTLECHIP_STATE_COMMAND:
reply = ok;
break;
case BATTLECHIP_STATE_UNK_0:
case BATTLECHIP_STATE_UNK_1:
reply = 0xFFFF;
break;
case BATTLECHIP_STATE_DATA_0:
reply = gate->data[0];
gate->data[0] += 3;
gate->data[0] &= 0x00FF;
break;
case BATTLECHIP_STATE_DATA_1:
reply = gate->data[1];
gate->data[1] -= 3;
gate->data[1] |= 0xFC00;
break;
case BATTLECHIP_STATE_ID:
reply = gate->chipId;
break;
case BATTLECHIP_STATE_UNK_2:
case BATTLECHIP_STATE_UNK_3:
reply = 0;
break;
case BATTLECHIP_STATE_END:
reply = ok;
gate->state = BATTLECHIP_STATE_SYNC;
break;
}
mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state);
++gate->state;
gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;

View File

@ -164,6 +164,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
}
break;
case SIO_MULTI:
value &= 0xFF83;
value |= 0xC;
break;
default:

View File

@ -137,7 +137,7 @@ void AssetTile::selectColor(int index) {
mTileCache* tileCache = m_tileCaches[m_index >= m_boundary];
unsigned bpp = 8 << tileCache->bpp;
int paletteId = m_paletteId;
data = mTileCacheGetTile(tileCache, m_index, m_paletteId);
data = mTileCacheGetTile(tileCache, m_index >= m_boundary ? m_index - m_boundary : m_index, m_paletteId);
color_t color = data[index];
m_ui.color->setColor(0, color);
m_ui.color->update();

View File

@ -7,6 +7,9 @@
#include "CoreController.h"
#include <QFile>
#include <QStringList>
using namespace QGBA;
BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, QWidget* parent)
@ -15,22 +18,97 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, QWidg
{
m_ui.setupUi(this);
char title[9];
CoreController::Interrupter interrupter(m_controller);
mCore* core = m_controller->thread()->core;
title[8] = '\0';
core->getGameCode(core, title);
QString qtitle(title);
connect(m_ui.chipId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_ui.inserted, [this]() {
m_ui.inserted->setChecked(Qt::Checked);
insertChip(true);
m_ui.inserted->setChecked(Qt::Unchecked);
});
connect(m_ui.chipName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_ui.chipId, [this](int id) {
m_ui.chipId->setValue(m_chipIndexToId[id]);
});
connect(m_ui.inserted, &QAbstractButton::toggled, this, &BattleChipView::insertChip);
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
connect(m_ui.gateBattleChip, &QAbstractButton::toggled, this, [this](bool on) {
if (on) {
setFlavor(GBA_FLAVOR_BATTLECHIP_GATE);
}
});
connect(m_ui.gateProgress, &QAbstractButton::toggled, this, [this](bool on) {
if (on) {
setFlavor(GBA_FLAVOR_PROGRESS_GATE);
}
});
connect(m_ui.gateBeastLink, &QAbstractButton::toggled, this, [this, qtitle](bool on) {
if (on) {
if (qtitle.endsWith('E') || qtitle.endsWith('P')) {
setFlavor(GBA_FLAVOR_BEAST_LINK_GATE_US);
} else {
setFlavor(GBA_FLAVOR_BEAST_LINK_GATE);
}
}
});
m_controller->attachBattleChipGate();
setFlavor(4);
if (qtitle.startsWith("AGB-B4B") || qtitle.startsWith("AGB-B4W") || qtitle.startsWith("AGB-BR4") || qtitle.startsWith("AGB-BZ3")) {
m_ui.gateBattleChip->setChecked(Qt::Checked);
} else if (qtitle.startsWith("AGB-BRB") || qtitle.startsWith("AGB-BRK")) {
m_ui.gateProgress->setChecked(Qt::Checked);
} else if (qtitle.startsWith("AGB-BR5") || qtitle.startsWith("AGB-BR6")) {
m_ui.gateBeastLink->setChecked(Qt::Checked);
}
}
BattleChipView::~BattleChipView() {
m_controller->detachBattleChipGate();
}
void BattleChipView::setFlavor(int flavor) {
m_controller->setBattleChipFlavor(flavor);
loadChipNames(flavor);
}
void BattleChipView::insertChip(bool inserted) {
if (inserted) {
m_controller->setBattleChipId(m_ui.chipId->value());
} else {
m_controller->setBattleChipId(0);
}
}
void BattleChipView::loadChipNames(int flavor) {
QStringList chipNames;
chipNames.append(tr("(None)"));
m_chipIndexToId.clear();
if (flavor == GBA_FLAVOR_BEAST_LINK_GATE_US) {
flavor = GBA_FLAVOR_BEAST_LINK_GATE;
}
QFile file(QString(":/res/chip-names-%1.txt").arg(flavor));
file.open(QIODevice::ReadOnly | QIODevice::Text);
int id = 0;
while (true) {
QByteArray line = file.readLine();
if (line.isEmpty()) {
break;
}
++id;
if (line.trimmed().isEmpty()) {
continue;
}
m_chipIndexToId[chipNames.length()] = id;
chipNames.append(QString::fromUtf8(line).trimmed());
}
m_ui.chipName->clear();
m_ui.chipName->addItems(chipNames);
}

View File

@ -25,11 +25,15 @@ public:
~BattleChipView();
public slots:
void setFlavor(int);
void insertChip(bool);
private:
void loadChipNames(int);
Ui::BattleChipView m_ui;
QMap<int, int> m_chipIndexToId;
std::shared_ptr<CoreController> m_controller;
};

View File

@ -6,40 +6,90 @@
<rect>
<x>0</x>
<y>0</y>
<width>217</width>
<height>100</height>
<width>426</width>
<height>278</height>
</rect>
</property>
<property name="windowTitle">
<string>BattleChip Gate</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="1">
<item row="5" column="1">
<widget class="QCheckBox" name="inserted">
<property name="text">
<string>Inserted</string>
</property>
</widget>
</item>
<item row="0" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Chip ID</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="chipId">
<property name="minimum">
<number>1</number>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Chip name</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="chipId">
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="chipName"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Gate type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="gateProgress">
<property name="text">
<string>Progress &amp;Gate</string>
</property>
<attribute name="buttonGroup">
<string notr="true">gate</string>
</attribute>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="gateBattleChip">
<property name="text">
<string>Ba&amp;ttleChip Gate</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">gate</string>
</attribute>
</widget>
</item>
<item row="2" column="1">
<widget class="QRadioButton" name="gateBeastLink">
<property name="text">
<string>Beast &amp;Link Gate</string>
</property>
<attribute name="buttonGroup">
<string notr="true">gate</string>
</attribute>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="gate"/>
</buttongroups>
</ui>

View File

@ -738,6 +738,14 @@ void CoreController::setBattleChipId(uint16_t id) {
Interrupter interrupter(this);
m_battlechip.chipId = id;
}
void CoreController::setBattleChipFlavor(int flavor) {
if (platform() != PLATFORM_GBA) {
return;
}
Interrupter interrupter(this);
m_battlechip.flavor = flavor;
}
#endif
void CoreController::setAVStream(mAVStream* stream) {

View File

@ -140,6 +140,7 @@ public slots:
void attachBattleChipGate();
void detachBattleChipGate();
void setBattleChipId(uint16_t id);
void setBattleChipFlavor(int flavor);
#endif
void setAVStream(mAVStream*);

View File

@ -854,6 +854,11 @@ void Window::gameStopped() {
m_fpsTimer.stop();
m_focusCheck.stop();
if (m_audioProcessor) {
m_audioProcessor->stop();
m_audioProcessor.reset();
}
emit paused(false);
}
@ -1428,12 +1433,7 @@ void Window::setupMenu(QMenuBar* menubar) {
#ifdef M_CORE_GBA
QAction* bcGate = new QAction(tr("BattleChip Gate..."), emulationMenu);
connect(bcGate, &QAction::triggered, [this]() {
BattleChipView* view = new BattleChipView(m_controller);
openView(view);
m_controller->attachBattleChipGate();
});
connect(bcGate, &QAction::triggered, openControllerTView<BattleChipView>());
addControlledAction(emulationMenu, bcGate, "bcGate");
m_platformActions.append(qMakePair(bcGate, SUPPORT_GBA));
m_gameActions.append(bcGate);

View File

@ -4,5 +4,8 @@
<file>../../../res/patrons.txt</file>
<file>../../../res/no-cam.png</file>
<file>input/default-profiles.ini</file>
<file>../../../res/chip-names-4.txt</file>
<file>../../../res/chip-names-5.txt</file>
<file>../../../res/chip-names-6.txt</file>
</qresource>
</RCC>

View File

@ -463,7 +463,7 @@ int32_t _readGyroZ(struct mRotationSource* source) {
UNUSED(source);
SixAxisSensorValues sixaxis;
hidSixAxisSensorValuesRead(&sixaxis, CONTROLLER_P1_AUTO, 1);
return sixaxis.gyroscope.z * 1.1e9f;
return sixaxis.gyroscope.z * -1.1e9f;
}
static int _batteryState(void) {

View File

@ -548,9 +548,7 @@ int main(int argc, char* argv[]) {
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
@ -1074,8 +1072,15 @@ static s8 WPAD_StickX(u8 chan, u8 right) {
return 0;
}
int centered = (int) js->pos.x - (int) js->center.x;
int range = js->max.x - js->min.x;
return (centered * 0xFF) / range;
int range = (int) js->max.x - (int) js->min.x;
int value = (centered * 0xFF) / range;
if (value > 0x7F) {
return 0x7F;
}
if (value < -0x80) {
return -0x80;
}
return value;
}
static s8 WPAD_StickY(u8 chan, u8 right) {
@ -1105,8 +1110,15 @@ static s8 WPAD_StickY(u8 chan, u8 right) {
return 0;
}
int centered = (int) js->pos.y - (int) js->center.y;
int range = js->max.y - js->min.y;
return (centered * 0xFF) / range;
int range = (int) js->max.y - (int) js->min.y;
int value = (centered * 0xFF) / range;
if (value > 0x7F) {
return 0x7F;
}
if (value < -0x80) {
return -0x80;
}
return value;
}
void _retraceCallback(u32 count) {