Merge branch 'master' (early part) into medusa
jump to
@@ -21,6 +21,7 @@
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 @@ - Switch: Fix final cleanup (fixes mgba.io/i/1283)
- 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
@@ -60,11 +60,20 @@
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*);
@@ -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
@@ -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
@@ -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
@@ -113,7 +113,7 @@ mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
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; }
@@ -557,6 +557,7 @@ gba->luminanceSource = periph;
break; case mPERIPH_GBA_BATTLECHIP_GATE: GBASIOSetDriver(&gba->sio, periph, SIO_MULTI); + GBASIOSetDriver(&gba->sio, periph, SIO_NORMAL_32); break; default: return;
@@ -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 @@
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 @@ return value;
} 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 @@ gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
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); + + uint16_t ok; + switch (gate->flavor) { + case GBA_FLAVOR_BATTLECHIP_GATE: + default: + ok = BATTLECHIP_OK; + break; + 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; + } - switch (cmd) { - case 0x4000: - gate->index = 0; - // Fall through - case 0: - switch (gate->index) { - case BATTLECHIP_INDEX_HANDSHAKE_0: - reply = 0x00FE; + 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; - 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; + } + } + + switch (gate->state) { + case BATTLECHIP_STATE_SYNC: + if (cmd != 0x8FFF) { + --gate->state; } - ++gate->index; + // 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 0x8FFF: - gate->index = -2; - // Fall through - default: - case 0xA3D0: - reply = BATTLECHIP_OK; + case BATTLECHIP_STATE_UNK_2: + case BATTLECHIP_STATE_UNK_3: + reply = 0; break; - case 0x4234: - case 0x574A: - reply = BATTLECHIP_CONTINUE; + case BATTLECHIP_STATE_END: + reply = ok; + gate->state = BATTLECHIP_STATE_SYNC; break; } - mLOG(GBA_BATTLECHIP, DEBUG, "< %04x", reply); + mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state); + ++gate->state; gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
@@ -164,6 +164,7 @@ value &= ~0x0080;
} break; case SIO_MULTI: + value &= 0xFF83; value |= 0xC; break; default:
@@ -137,7 +137,7 @@ const color_t* data;
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();
@@ -7,6 +7,9 @@ #include "BattleChipView.h"
#include "CoreController.h" +#include <QFile> +#include <QStringList> + using namespace QGBA; BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, QWidget* parent)@@ -14,23 +17,98 @@ : QDialog(parent)
, m_controller(controller) { 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); }
@@ -25,11 +25,15 @@ BattleChipView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
~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; };
@@ -6,40 +6,90 @@ <property name="geometry">
<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"> + <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="minimum"> - <number>1</number> - </property> <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 &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&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 &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>
@@ -738,6 +738,14 @@ }
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) {
@@ -140,6 +140,7 @@ #ifdef M_CORE_GBA
void attachBattleChipGate(); void detachBattleChipGate(); void setBattleChipId(uint16_t id); + void setBattleChipFlavor(int flavor); #endif void setAVStream(mAVStream*);
@@ -854,6 +854,11 @@
m_fpsTimer.stop(); m_focusCheck.stop(); + if (m_audioProcessor) { + m_audioProcessor->stop(); + m_audioProcessor.reset(); + } + emit paused(false); }@@ -1428,12 +1433,7 @@ #endif
#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);
@@ -4,5 +4,8 @@ <file>../../../res/medusa-bg.jpg</file>
<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>
@@ -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) {
@@ -548,9 +548,7 @@ _mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
_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 @@ if (!js) {
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 @@ if (!js) {
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) {