Video is working

This commit is contained in:
jommg 2026-04-14 16:34:24 +02:00
parent 3ba0416dc6
commit 5b7fc2cab8
53 changed files with 8804 additions and 1313 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@
*.diff
*.dump
*.elf
*.dll
*.exe
*.fwjpnfont
*.gba

View File

@ -23,7 +23,9 @@ OBJDUMP := $(PREFIX)objdump
AS := $(PREFIX)as
LD := $(PREFIX)ld
MODERN := 1
SDL_DIR := /mnt/c/Users/josma/Desktop/pokexe/pokefirered/sdl
SDL_DIR := /mnt/c/Users/josma/Desktop/pokexe2/SDL2-2.32.10/i686-w64-mingw32
EXE :=
ifeq ($(OS),Windows_NT)
@ -371,5 +373,5 @@ endif
# Elf from object files
LDFLAGS = -Map ../../$(MAP)
$(ROM): $(LD_SCRIPT) $(LD_SCRIPT_DEPS) $(OBJS)
cd $(OBJ_DIR) && i686-w64-mingw32-gcc -Wno-trigraphs -Wimplicit -Wparentheses -Wunused -m32 -std=gnu99 -fleading-underscore -fno-dce -fno-builtin -Wno-unused-function -DFIRERED=1 -DREVISION=0 -DENGLISH=1 -DPORTABLE -DNONMATCHING -D UBFIX -DMODERN=1 -O3 -Wl,--demangle $(OBJS_REL) -o $(ROM) -L$(SDL_DIR)/lib -lxinput -lkernel32 -lSDL3
cd $(OBJ_DIR) && i686-w64-mingw32-gcc -Wno-trigraphs -Wimplicit -Wparentheses -Wunused -m32 -std=gnu99 -fleading-underscore -fno-dce -fno-builtin -Wno-unused-function -DFIRERED=1 -DREVISION=0 -DENGLISH=1 -DPORTABLE -DNONMATCHING -D UBFIX -DMODERN=1 -O3 -Wl,--demangle $(OBJS_REL) -o $(ROM) -L$(SDL_DIR)/lib -lxinput -lkernel32 -lmingw32 -lSDL2main -lSDL2
mv $(OBJ_DIR)/$(ROM) ./

36
include/cgb_audio.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef CGB_AUDIO_H
#define CGB_AUDIO_H
#define MIXED_AUDIO_BUFFER_SIZE 4907
struct AudioCGB{
u16 ch1Freq;
u8 ch1SweepCounter;
u8 ch1SweepCounterI;
bool8 ch1SweepDir;
u8 ch1SweepShift;
u8 Vol[4];
u8 VolI[4];
u8 Len[4];
u8 LenI[4];
bool8 LenOn[4];
u8 EnvCounter[4];
u8 EnvCounterI[4];
bool8 EnvDir[4];
bool8 DAC[4];
float WAVRAM[32];
u16 ch4LFSR [2];
__attribute__((aligned(4))) float outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2];
};
void cgb_audio_init(u32 rate);
void cgb_set_sweep(u8 sweep);
void cgb_set_wavram();
void cgb_toggle_length(u8 channel, bool8 state);
void cgb_set_length(u8 channel, u8 length);
void cgb_set_envelope(u8 channel, u8 envelope);
void cgb_trigger_note(u8 channel);
void cgb_audio_generate(u16 samplesPerFrame);
float *cgb_get_buffer();
#endif

2349
include/cgb_tables.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -24,14 +24,15 @@ extern void * INTR_VECTOR;
#define OBJ_PLTT_SIZE 0x200
#define PLTT_SIZE (BG_PLTT_SIZE + OBJ_PLTT_SIZE)
extern u8 PLTT[PLTT_SIZE];
extern u8 PLTT[PLTT_SIZE] __attribute__ ((aligned (4)));
#define BG_PLTT PLTT
#define OBJ_PLTT (PLTT + BG_PLTT_SIZE)
#define VRAM_SIZE 0x18000
extern u32 VRAM[VRAM_SIZE / sizeof(u32)];
extern u8 VRAM_[VRAM_SIZE] __attribute__ ((aligned (4)));
#define VRAM (u32)VRAM_
#define BG_VRAM VRAM
#define BG_VRAM_SIZE 0x10000
@ -54,7 +55,7 @@ extern u32 VRAM[VRAM_SIZE / sizeof(u32)];
#define OBJ_VRAM1_SIZE 0x4000
#define OAM_SIZE 0x400
extern u8 OAM[OAM_SIZE];
extern u8 OAM[OAM_SIZE] __attribute__ ((aligned (4)));
#define ROM_HEADER_SIZE 0xC0

View File

@ -2,7 +2,7 @@
#define GUARD_GBA_FLASH_INTERNAL_H
#define FLASH_ROM_SIZE_1M 131072 // 1 megabit ROM
extern u8 FLASH_BASE[FLASH_ROM_SIZE_1M];
extern u8 FLASH_BASE[FLASH_ROM_SIZE_1M] __attribute__ ((aligned (4)));
#define FLASH_WRITE(addr, data) ((*(vu8 *)(FLASH_BASE + (addr))) = (data))
@ -44,11 +44,11 @@ struct FlashSetupInfo
extern u16 gFlashNumRemainingBytes;
extern u16 (*ProgramFlashByte)(u16, u32, u8);
extern u16 (*ProgramFlashSector)(u16, void *);
extern u16 (*EraseFlashChip)(void);
extern u16 (*EraseFlashSector)(u16);
extern u16 (*WaitForFlashWrite)(u8, u8 *, u8);
extern u16 ProgramFlashByte(u16, u32, u8);
extern u16 ProgramFlashSector(u16, void *);
extern u16 EraseFlashChip(void);
extern u16 EraseFlashSector(u16);
extern u16 WaitForFlashWrite(u8, u8 *, u8);
extern const u16 *gFlashMaxTime;
extern const struct FlashType *gFlash;
@ -60,7 +60,6 @@ extern const struct FlashSetupInfo LE26FV10N1TS;
extern const struct FlashSetupInfo DefaultFlash;
void SwitchFlashBank(u8 bankNum);
u16 ReadFlashId(void);
void StartFlashTimer(u8 phase);
void SetReadFlash1(u16 *dest);
void StopFlashTimer(void);

View File

@ -1,7 +1,7 @@
#ifndef GUARD_GBA_IO_REG_H
#define GUARD_GBA_IO_REG_H
extern u8 REG_BASE[];
extern u8 REG_BASE[0x400] __attribute__ ((aligned (4)));
// I/O register offsets
@ -643,9 +643,11 @@ extern u8 REG_BASE[];
#define DMA_DEST_DEC 0x0020
#define DMA_DEST_FIXED 0x0040
#define DMA_DEST_RELOAD 0x0060
#define DMA_DEST_MASK 0x0060
#define DMA_SRC_INC 0x0000
#define DMA_SRC_DEC 0x0080
#define DMA_SRC_FIXED 0x0100
#define DMA_SRC_MASK 0x0180
#define DMA_REPEAT 0x0200
#define DMA_16BIT 0x0000
#define DMA_32BIT 0x0400
@ -773,4 +775,6 @@ extern u8 REG_BASE[];
#define WAITCNT_AGB (0 << 15)
#define WAITCNT_CGB (1 << 15)
void printRegs();
#endif // GUARD_GBA_IO_REG_H

View File

@ -2,6 +2,7 @@
#define GUARD_GBA_M4A_INTERNAL_H
#include "gba/gba.h"
#include "music_player.h"
// ASCII encoding of 'Smsh' in reverse
// This is presumably short for SMASH, the developer of MKS4AGB.
@ -152,7 +153,7 @@ struct SoundChannel
u8 rhythmPan;
u8 dummy3[3];
u32 count;
u32 fw;
float fw;
u32 frequency;
struct WaveData *wav;
s8 *currentPointer;
@ -165,8 +166,7 @@ struct SoundChannel
};
#define MAX_DIRECTSOUND_CHANNELS 12
#define PCM_DMA_BUF_SIZE 1584 // size of Direct Sound buffer
#define PCM_DMA_BUF_SIZE 4907 // size of Direct Sound buffer
struct MusicPlayerInfo;
@ -205,7 +205,7 @@ struct SoundInfo
u8 gap[3];
s32 pcmSamplesPerVBlank;
s32 pcmFreq;
s32 divFreq;
float divFreq;
struct CgbChannel *cgbChans;
MPlayMainFunc MPlayMainHead;
struct MusicPlayerInfo *musicPlayerHead;
@ -217,7 +217,7 @@ struct SoundInfo
ExtVolPitFunc ExtVolPit;
u8 gap2[16];
struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS];
s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2];
float pcmBuffer[PCM_DMA_BUF_SIZE * 2];
};
struct SongHeader
@ -408,8 +408,8 @@ extern const struct ToneData voicegroup000;
u32 umul3232H32(u32 multiplier, u32 multiplicand);
void SoundMain(void);
void SoundMainBTM(void);
void TrackStop(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track);
void SoundMainBTM(void *ptr);
void TrackStop(struct MP2KPlayerState *player, struct MP2KTrack *track);
void MPlayMain(struct MusicPlayerInfo *);
void RealClearChain(void *x);
@ -430,7 +430,7 @@ void CgbOscOff(u8);
void CgbModVol(struct CgbChannel *chan);
u32 MidiKeyToCgbFreq(u8, u8, u8);
void DummyFunc(void);
void MPlayJumpTableCopy(MPlayFunc *mplayJumpTable);
void MPlayJumpTableCopy(void **mplayJumpTable);
void SampleFreqSet(u32 freq);
void m4aSoundVSyncOn(void);
void m4aSoundVSyncOff(void);
@ -456,29 +456,29 @@ void SetPokemonCryStereo(u32 val);
void SetPokemonCryPriority(u8 val);
// sound command handler functions
void ply_fine(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_goto(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_patt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_pend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_rept(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void MP2K_event_fine(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_goto(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_patt(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_pend(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_rept(struct MP2KPlayerState *, struct MP2KTrack *);
void ply_memacc(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_prio(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_tempo(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_keysh(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_voice(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_vol(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_pan(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_bend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_bendr(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_lfos(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_lfodl(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_mod(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_modt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_tune(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_port(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void MP2K_event_prio(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_tempo(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_keysh(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_voice(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_vol(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_pan(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_bend(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_bendr(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_lfos(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_lfodl(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_mod(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_modt(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_tune(struct MP2KPlayerState *, struct MP2KTrack *);
void MP2K_event_port(struct MP2KPlayerState *, struct MP2KTrack *);
void ply_xcmd(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_endtie(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void ply_note(u32 note_cmd, struct MusicPlayerInfo *, struct MusicPlayerTrack *);
void MP2K_event_endtie(struct MP2KPlayerState *, struct MP2KTrack *);
void ply_note(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
// extended sound command handler functions
void ply_xxx(struct MusicPlayerInfo *, struct MusicPlayerTrack *);

View File

@ -31,14 +31,7 @@
#define CpuFastCopy(src, dest, size) CpuFastSet(src, dest, ((size)/(32/8) & 0x1FFFFF))
#define DmaSet(dmaNum, src, dest, control) \
{ \
vu32 *dmaRegs = (vu32 *)REG_ADDR_DMA##dmaNum; \
dmaRegs[0] = (vu32)(src); \
dmaRegs[1] = (vu32)(dest); \
dmaRegs[2] = (vu32)(control); \
dmaRegs[2]; \
}
extern void DmaSet(int dmaNum, const void * src, void * dest, u32 control);
#define DMA_FILL(dmaNum, value, dest, size, bit) \
{ \
@ -50,8 +43,8 @@
| ((size)/(bit/8))); \
}
#define DmaFill16(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 16)
#define DmaFill32(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 32)
#define DmaFill16(dmaNum, value, dest, size) CpuFill16(value, dest, size)
#define DmaFill32(dmaNum, value, dest, size) CpuFill32(value, dest, size)
// Note that the DMA clear macros cause the DMA control value to be calculated
// at runtime rather than compile time. The size is divided by the DMA transfer
@ -75,8 +68,8 @@
(DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_INC | DMA_DEST_INC) << 16 \
| ((size)/(bit/8)))
#define DmaCopy16(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 16)
#define DmaCopy32(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 32)
#define DmaCopy16(dmaNum, src, dest, size) CpuCopy16(src, dest, size)
#define DmaCopy32(dmaNum, src, dest, size) CpuCopy32(src, dest, size)
#define DmaStop(dmaNum) \
{ \

View File

@ -31,6 +31,13 @@ typedef vu8 vbool8;
typedef vu16 vbool16;
typedef vu32 vbool32;
typedef int_fast8_t sf8;
typedef uint_fast8_t uf8;
typedef int_fast16_t sf16;
typedef uint_fast16_t uf16;
typedef int_fast32_t sf32;
typedef uint_fast32_t uf32;
struct BgCnt
{
u16 priority:2;

View File

@ -16,7 +16,7 @@
#define TRY_FREE_AND_SET_NULL(ptr) if (ptr != NULL) FREE_AND_SET_NULL(ptr)
extern u8 gHeap[HEAP_SIZE];
extern ALIGNED(4) u8 gHeap[HEAP_SIZE];
void *Alloc(u32 size);
void *AllocZeroed(u32 size);
void Free(void *pointer);

19
include/mp2k_common.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef MP2020_COMMON_H
#define MP2020_COMMON_H
#ifndef __has_builtin
#define __has_builtin(x) defined(__GNUC__)
#endif
#if ((-1 >> 1) == -1) && __has_builtin(__builtin_ctz)
#define FLOOR_DIV_POW2(a, b) ((a) >> __builtin_ctz(b))
#else
#define FLOOR_DIV_POW2(a, b) ((a) > 0 ? (a) / (b) : (((a) + 1 - (b)) / (b)))
#endif
#define NOT_GBA_BIOS
#define NOT_GBA
//#define ORIGINAL_COARSE_POSITION_CLEARING
#define POKEMON_EXTENSIONS
#endif

127
include/music_player.h Normal file
View File

@ -0,0 +1,127 @@
#ifndef MUSIC_PLAYER_H
#define MUSIC_PLAYER_H
#include "gba/types.h"
//#include "m4a.h"
#include "sound_mixer.h"
#define PLAYER_UNLOCKED 0x68736D53
#define PLAYER_LOCKED PLAYER_UNLOCKED+1
struct WaveData2
{
u8 compressionFlags1;
u8 compressionFlags2;
u8 compressionFlags3;
u8 loopFlags;
u32 freq; // 22.10 fixed width decimal, freq of C4. Or just the frequency of C14.
u32 loopStart;
u32 size; // number of samples
s8 data[]; // samples
};
struct MP2KInstrument {
u8 type;
u8 drumKey;
u8 cgbLength;
u8 panSweep;
union {
struct WaveData *wav;
struct MP2KInstrument *group;
u32 *cgb3Sample;
u32 squareNoiseConfig;
};
union {
struct {
u8 attack;
u8 decay;
u8 sustain;
u8 release;
};
u8 *keySplitTable;
};
};
struct MP2KTrack {
u8 status;
u8 wait;
u8 patternLevel;
u8 repeatCount;
u8 gateTime; // 0 if TIE
u8 key;
u8 velocity;
u8 runningStatus;
s8 keyShiftCalculated; // Calculated by TrkVolPitSet using fields below. Units: semitones
u8 pitchCalculated; // Calculated by TrkVolPitSet using fields below. Units: 256ths of a semitone
s8 keyShift; // Units: semitones
s8 keyShiftPublic; // Units: semitones
s8 tune; // Units: 64ths of a semitone
u8 pitchPublic; // Units: 256ths of a semitone
s8 bend; // Units: (bendRange / 64)ths of a semitone
u8 bendRange;
u8 volRightCalculated;
u8 volLeftCalculated;
u8 vol;
u8 volPublic; // Used both for fades and MPlayVolumeControl
s8 pan;
s8 panPublic;
s8 modCalculated; // Pitch units: 16ths of a semitone
u8 modDepth;
u8 modType;
u8 lfoSpeed;
u8 lfoSpeedCounter;
u8 lfoDelay;
u8 lfoDelayCounter;
u8 priority;
u8 echoVolume;
u8 echoLength;
struct MixerSource *chan;
struct MP2KInstrument instrument;
u8 gap[10];
u16 unk_3A;
u32 ct;
u8 *cmdPtr;
u8 *patternStack[3];
};
struct MP2KPlayerState {
struct MP2KSongHeader *songHeader;
vu32 status;
u8 trackCount;
u8 priority;
u8 cmd;
bool8 checkSongPriority;
u32 clock;
u8 padding[8];
u8 *memaccArea;
u16 tempoRawBPM; // 150 initially... this doesn't seem right but whatever
u16 tempoScale; // 0x100 initially
u16 tempoInterval; // 150 initially
u16 tempoCounter;
u16 fadeInterval;
u16 fadeCounter;
u16 isFadeTemporary:1;
u16 isFadeIn:1;
u16 fadeVolume:7;
u16 :7; // padding
struct MP2KTrack *tracks;
struct MP2KInstrument *voicegroup;
vu32 lockStatus;
void (*nextPlayerFunc)(void *);
void *nextPlayer;
};
struct MP2KPlayerCtor {
struct MP2KPlayerState *player;
struct MP2KTrack *tracks;
u8 trackCount;
u8 padding;
bool16 checkSongPriority;
};
void clear_modM(struct MP2KPlayerState *unused, struct MP2KTrack *track);
void MP2K_event_endtie(struct MP2KPlayerState *unused, struct MP2KTrack *track);
void MP2K_event_lfos(struct MP2KPlayerState *unused, struct MP2KTrack *track);
void MP2K_event_mod(struct MP2KPlayerState *unused, struct MP2KTrack *track);
#endif

13
include/platform.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef GUARD_PLATFORM_H
#define GUARD_PLATFORM_H
#include "global.h"
void Platform_StoreSaveFile(void);
void Platform_ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size);
void Platform_QueueAudio(float *audioBuffer, s32 samplesPerFrame);
u16 Platform_GetKeyInput(void);
#endif

16
include/platform/dma.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef GUARD_DMA_H
#define GUARD_DMA_H
#include "global.h"
#define DMA_COUNT 4
enum {
DMA_NOW,
DMA_VBLANK,
DMA_HBLANK,
DMA_SPECIAL
};
void RunDMAs(u32 type);
#endif

View File

@ -0,0 +1,7 @@
#ifndef GUARD_FRAMEDRAW_H
#define GUARD_FRAMEDRAW_H
#include "global.h"
void DrawFrame(uint16_t *pixels);
#endif

121
include/sound_mixer.h Normal file
View File

@ -0,0 +1,121 @@
#ifndef SOUND_MIXER_H
#define SOUND_MIXER_H
#include "gba/types.h"
#include "music_player.h"
#define MIXER_UNLOCKED 0x68736D53
#define MIXER_LOCKED PLAYER_UNLOCKED+1
struct MP2KPlayerState;
struct MixerSource {
u8 status;
u8 type;
u8 rightVol;
u8 leftVol;
u8 attack;
u8 decay;
u8 sustain;
u8 release;
u8 key;
u8 envelopeVol;
union {
u8 envelopeVolR;
u8 envelopeGoal;
}__attribute__((packed));
union {
u8 envelopeVolL;
u8 envelopeCtr;
}__attribute__((packed));
u8 echoVol;
u8 echoLen;
u8 padding1;
u8 padding2;
u8 gateTime;
u8 untransposedKey;
u8 velocity;
u8 priority;
u8 rhythmPan;
u8 padding3;
u8 padding4;
u8 padding5;
union {
u32 ct;
struct {
u8 padding6;
u8 sustainGoal;
u8 nrx4;
u8 pan;
};
};
union {
float fw;
struct {
u8 panMask;
u8 cgbStatus;
u8 length;
u8 sweep;
};
};
u32 freq;
union {
u32 *newCgb3Sample;
struct WaveData *wav;
};
union {
u32 *oldCgb3Sample;
s8 *current;
};
struct MP2KTrack *track;
struct MixerSource *prev;
struct MixerSource *next;
u32 padding7; //d4
u32 blockCount; // bdpcm block count
};
enum { MAX_SAMPLE_CHANNELS = 12 };
enum { MIXED_AUDIO_BUFFER_SIZE = 4907 };
struct SoundMixerState {
vu32 lockStatus;
vu8 dmaCounter;
u8 reverb;
u8 numChans;
u8 masterVol;
u8 freqOption;
u8 extensionFlags;
u8 cgbCounter15;
u8 framesPerDmaCycle;
u8 maxScanlines;
u8 padding1;
u8 padding2;
u8 padding3;
s32 samplesPerFrame;
s32 sampleRate;
float sampleRateReciprocal;
struct MixerSource *cgbChans;
void (*firstPlayerFunc)(void *player);
void *firstPlayer;
void (*cgbMixerFunc)(void);
void (*cgbNoteOffFunc)(u8 chan);
u32 (*cgbCalcFreqFunc)(u8 chan, u8 key, u8 pitch);
void (**mp2kEventFuncTable)();
void (*mp2kEventNxxFunc)(u8 clock, struct MP2KPlayerState *player, struct MP2KTrack *track);
void *reserved1; // In poke* this is called "ExtVolPit"
void *reserved2;
void *reserved3;
void *reversed4;
void *reserved5;
struct MixerSource chans[MAX_SAMPLE_CHANNELS];
__attribute__((aligned(4))) float outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2];
//s8 outBuffer[MIXED_AUDIO_BUFFER_SIZE * 2];
};
typedef void (*MixerRamFunc)(struct SoundMixerState *, u32, u16, s8 *, u16);
#ifndef NOT_GBA
#undef REG_VCOUNT
#define REG_VCOUNT (*(vu8*)REG_ADDR_VCOUNT)
#endif
#endif//SOUND_MIXER_H

View File

@ -8,14 +8,70 @@ static u16 sSavedIme;
u8 gFlashTimeoutFlag = 0;
u8 (*PollFlashStatus)(u8 *) = NULL;
u16 (*WaitForFlashWrite)(u8 phase, u8 *addr, u8 lastData) = NULL;
u16 (*ProgramFlashSector)(u16 sectorNum, void *src) = NULL;
const struct FlashType *gFlash = NULL;
u16 (*ProgramFlashByte)(u16 sectorNum, u32 offset, u8 data) = NULL;
const u16 gFlashMaxTimeData[] =
{
10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
};
const struct FlashType gFlashData = {
131072, // ROM size
{
4096, // sector size
12, // bit shift to multiply by sector size (4096 == 1 << 12)
32, // number of sectors
0 // appears to be unused
},
{ 3, 1 }, // wait state setup data
{ { 0xCC, 0xCC } } // ID
};
u16 WaitForFlashWrite(u8 phase, u8 *addr, u8 lastData);
u16 ProgramFlashSector(u16 sectorNum, void *src);
u16 ProgramFlashByte(u16 sectorNum, u32 offset, u8 data);
u16 gFlashNumRemainingBytes = 0;
u16 (*EraseFlashChip)() = NULL;
u16 (*EraseFlashSector)(u16 sectorNum) = NULL;
const u16 *gFlashMaxTime = NULL;
u16 EraseFlashChip(void);
u16 EraseFlashSector(u16 sectorNum);
const u16 *gFlashMaxTime = gFlashMaxTimeData;
const struct FlashType *gFlash = &gFlashData;
u16 WaitForFlashWrite(u8 phase, u8 *addr, u8 lastData)
{
// stub
return 0;
}
u16 EraseFlashChip(void)
{
memset(FLASH_BASE, 0xFF, sizeof(FLASH_BASE));
return 0;
}
u16 EraseFlashSector(u16 sectorNum)
{
u8 clearBuffer[0x1000] = { 0xFF };
return ProgramFlashSector(sectorNum, &clearBuffer[0]);
}
u16 ProgramFlashByte(u16 sectorNum, u32 offset, u8 data)
{
FLASH_BASE[(sectorNum << gFlash->sector.shift) + offset] = data;
return 0;
}
u16 ProgramFlashSector(u16 sectorNum, void *src)
{
memcpy(&FLASH_BASE[sectorNum << gFlash->sector.shift], src, 0x1000);
return 0;
}
u16 IdentifyFlash(void)
{
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
return 0;
}
void SetReadFlash1(u16 *dest);
@ -34,33 +90,7 @@ do { \
; \
} while (0)
u16 ReadFlashId(void)
{
u16 flashId;
u16 readFlash1Buffer[0x20];
u8 (*readFlash1)(u8 *);
SetReadFlash1(readFlash1Buffer);
readFlash1 = (u8 (*)(u8 *))((s32)readFlash1Buffer + 1);
// Enter ID mode.
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
FLASH_WRITE(0x5555, 0x90);
DELAY();
flashId = readFlash1(FLASH_BASE + 1) << 8;
flashId |= readFlash1(FLASH_BASE);
// Leave ID mode.
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
FLASH_WRITE(0x5555, 0xF0);
FLASH_WRITE(0x5555, 0xF0);
DELAY();
return flashId;
}
void FlashTimerIntr(void)
{
@ -139,123 +169,23 @@ void ReadFlash_Core(vu8 *src, u8 *dest, u32 size)
void ReadFlash(u16 sectorNum, u32 offset, void *dest, u32 size)
{
u8 *src;
u16 i;
vu16 readFlash_Core_Buffer[0x40];
vu16 *funcSrc;
vu16 *funcDest;
void (*readFlash_Core)(vu8 *, u8 *, u32);
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
if (gFlash->romSize == FLASH_ROM_SIZE_1M)
{
SwitchFlashBank(sectorNum / SECTORS_PER_BANK);
sectorNum %= SECTORS_PER_BANK;
}
funcSrc = (vu16 *)ReadFlash_Core;
funcSrc = (vu16 *)((s32)funcSrc ^ 1);
funcDest = readFlash_Core_Buffer;
i = ((s32)ReadFlash - (s32)ReadFlash_Core) >> 1;
while (i != 0)
{
*funcDest++ = *funcSrc++;
i--;
}
readFlash_Core = (void (*)(vu8 *, u8 *, u32))((s32)readFlash_Core_Buffer + 1);
src = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset;
readFlash_Core(src, dest, size);
Platform_ReadFlash(sectorNum, offset, dest, size);
return;
}
u32 VerifyFlashSector_Core(u8 *src, u8 *tgt, u32 size)
{
while (size-- != 0)
{
if (*tgt++ != *src++)
return (u32)(tgt - 1);
}
return 0;
}
u32 VerifyFlashSector(u16 sectorNum, u8 *src)
{
u16 i;
vu16 verifyFlashSector_Core_Buffer[0x80];
vu16 *funcSrc;
vu16 *funcDest;
u8 *tgt;
u16 size;
u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32);
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
if (gFlash->romSize == FLASH_ROM_SIZE_1M)
{
SwitchFlashBank(sectorNum / SECTORS_PER_BANK);
sectorNum %= SECTORS_PER_BANK;
}
funcSrc = (vu16 *)VerifyFlashSector_Core;
funcSrc = (vu16 *)((s32)funcSrc ^ 1);
funcDest = verifyFlashSector_Core_Buffer;
i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1;
while (i != 0)
{
*funcDest++ = *funcSrc++;
i--;
}
verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1);
tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift);
size = gFlash->sector.size;
return verifyFlashSector_Core(src, tgt, size); // return 0 if verified.
return 0; // return 0 if verified.
}
u32 VerifyFlashSectorNBytes(u16 sectorNum, u8 *src, u32 n)
{
u16 i;
vu16 verifyFlashSector_Core_Buffer[0x80];
vu16 *funcSrc;
vu16 *funcDest;
u8 *tgt;
u32 (*verifyFlashSector_Core)(u8 *, u8 *, u32);
if (gFlash->romSize == FLASH_ROM_SIZE_1M)
{
SwitchFlashBank(sectorNum / SECTORS_PER_BANK);
sectorNum %= SECTORS_PER_BANK;
}
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
funcSrc = (vu16 *)VerifyFlashSector_Core;
funcSrc = (vu16 *)((s32)funcSrc ^ 1);
funcDest = verifyFlashSector_Core_Buffer;
i = ((s32)VerifyFlashSector - (s32)VerifyFlashSector_Core) >> 1;
while (i != 0)
{
*funcDest++ = *funcSrc++;
i--;
}
verifyFlashSector_Core = (u32 (*)(u8 *, u8 *, u32))((s32)verifyFlashSector_Core_Buffer + 1);
tgt = FLASH_BASE + (sectorNum << gFlash->sector.shift);
return verifyFlashSector_Core(src, tgt, n);
return 0; // return 0 if verified.
}
u32 ProgramFlashSectorAndVerify(u16 sectorNum, u8 *src)

View File

@ -1,86 +0,0 @@
#include "gba/gba.h"
#include "gba/flash_internal.h"
static const char AgbLibFlashVersion[] = "FLASH1M_V103";
const struct FlashSetupInfo * const sSetupInfos[] =
{
&MX29L010,
&LE26FV10N1TS,
&DefaultFlash
};
u16 IdentifyFlash(void)
{
u16 result;
u16 flashId;
const struct FlashSetupInfo * const *setupInfo;
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
flashId = ReadFlashId();
setupInfo = sSetupInfos;
result = 1;
for (;;)
{
if ((*setupInfo)->type.ids.separate.makerId == 0)
break;
if (flashId == (*setupInfo)->type.ids.joined)
{
result = 0;
break;
}
setupInfo++;
}
ProgramFlashByte = (*setupInfo)->programFlashByte;
ProgramFlashSector = (*setupInfo)->programFlashSector;
EraseFlashChip = (*setupInfo)->eraseFlashChip;
EraseFlashSector = (*setupInfo)->eraseFlashSector;
WaitForFlashWrite = (*setupInfo)->WaitForFlashWrite;
gFlashMaxTime = (*setupInfo)->maxTime;
gFlash = &(*setupInfo)->type;
return result;
}
u16 WaitForFlashWrite_Common(u8 phase, u8 *addr, u8 lastData)
{
u16 result = 0;
u8 status;
StartFlashTimer(phase);
while ((status = PollFlashStatus(addr)) != lastData)
{
if (status & 0x20)
{
// The write operation exceeded the flash chip's time limit.
if (PollFlashStatus(addr) == lastData)
break;
FLASH_WRITE(0x5555, 0xF0);
result = phase | 0xA000u;
break;
}
if (gFlashTimeoutFlag)
{
if (PollFlashStatus(addr) == lastData)
break;
FLASH_WRITE(0x5555, 0xF0);
result = phase | 0xC000u;
break;
}
}
StopFlashTimer();
return result;
}

View File

@ -1,31 +0,0 @@
#include "gba/gba.h"
#include "gba/flash_internal.h"
const u16 leMaxTime[] =
{
10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
};
const struct FlashSetupInfo LE26FV10N1TS =
{
ProgramFlashByte_MX,
ProgramFlashSector_MX,
EraseFlashChip_MX,
EraseFlashSector_MX,
WaitForFlashWrite_Common,
leMaxTime,
{
131072, // ROM size
{
4096, // sector size
12, // bit shift to multiply by sector size (4096 == 1 << 12)
32, // number of sectors
0 // appears to be unused
},
{ 3, 1 }, // wait state setup data
{ { 0x62, 0x13 } } // ID
}
};

View File

@ -1,197 +0,0 @@
#include "gba/gba.h"
#include "gba/flash_internal.h"
const u16 mxMaxTime[] =
{
10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
10, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
2000, 65469, TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_256CLK,
};
const struct FlashSetupInfo MX29L010 =
{
ProgramFlashByte_MX,
ProgramFlashSector_MX,
EraseFlashChip_MX,
EraseFlashSector_MX,
WaitForFlashWrite_Common,
mxMaxTime,
{
131072, // ROM size
{
4096, // sector size
12, // bit shift to multiply by sector size (4096 == 1 << 12)
32, // number of sectors
0 // appears to be unused
},
{ 3, 1 }, // wait state setup data
#if defined(GERMAN) && defined(SAPPHIRE)
{ { 0xBF, 0xD4 } } // ID
#else
{ { 0xC2, 0x09 } } // ID
#endif
}
};
const struct FlashSetupInfo DefaultFlash =
{
ProgramFlashByte_MX,
ProgramFlashSector_MX,
EraseFlashChip_MX,
EraseFlashSector_MX,
WaitForFlashWrite_Common,
mxMaxTime,
{
131072, // ROM size
{
4096, // sector size
12, // bit shift to multiply by sector size (4096 == 1 << 12)
32, // number of sectors
0 // appears to be unused
},
{ 3, 1 }, // wait state setup data
{ { 0x00, 0x00 } } // ID of 0
}
};
u16 EraseFlashChip_MX(void)
{
u16 result;
u16 readFlash1Buffer[0x20];
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0];
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
FLASH_WRITE(0x5555, 0x80);
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
FLASH_WRITE(0x5555, 0x10);
SetReadFlash1(readFlash1Buffer);
result = WaitForFlashWrite(3, FLASH_BASE, 0xFF);
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
return result;
}
u16 EraseFlashSector_MX(u16 sectorNum)
{
u16 numTries;
u16 result;
u8 *addr;
u16 readFlash1Buffer[0x20];
if (sectorNum >= gFlash->sector.count)
return 0x80FF;
SwitchFlashBank(sectorNum / SECTORS_PER_BANK);
sectorNum %= SECTORS_PER_BANK;
numTries = 0;
try_erase:
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0];
addr = FLASH_BASE + (sectorNum << gFlash->sector.shift);
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
FLASH_WRITE(0x5555, 0x80);
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
*addr = 0x30;
SetReadFlash1(readFlash1Buffer);
result = WaitForFlashWrite(2, addr, 0xFF);
if (!(result & 0xA000) || numTries > 3)
goto done;
numTries++;
goto try_erase;
done:
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | WAITCNT_SRAM_8;
return result;
}
u16 ProgramFlashByte_MX(u16 sectorNum, u32 offset, u8 data)
{
u8 *addr;
u16 readFlash1Buffer[0x20];
if (offset >= gFlash->sector.size)
return 0x8000;
SwitchFlashBank(sectorNum / SECTORS_PER_BANK);
sectorNum %= SECTORS_PER_BANK;
addr = FLASH_BASE + (sectorNum << gFlash->sector.shift) + offset;
SetReadFlash1(readFlash1Buffer);
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0];
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
FLASH_WRITE(0x5555, 0xA0);
*addr = data;
return WaitForFlashWrite(1, addr, data);
}
static u16 ProgramByte(u8 *src, u8 *dest)
{
FLASH_WRITE(0x5555, 0xAA);
FLASH_WRITE(0x2AAA, 0x55);
FLASH_WRITE(0x5555, 0xA0);
*dest = *src;
return WaitForFlashWrite(1, dest, *src);
}
u16 ProgramFlashSector_MX(u16 sectorNum, void *src)
{
u16 result;
u8 *dest;
u16 readFlash1Buffer[0x20];
if (sectorNum >= gFlash->sector.count)
return 0x80FF;
result = EraseFlashSector_MX(sectorNum);
if (result != 0)
return result;
SwitchFlashBank(sectorNum / SECTORS_PER_BANK);
sectorNum %= SECTORS_PER_BANK;
SetReadFlash1(readFlash1Buffer);
REG_WAITCNT = (REG_WAITCNT & ~WAITCNT_SRAM_MASK) | gFlash->wait[0];
gFlashNumRemainingBytes = gFlash->sector.size;
dest = FLASH_BASE + (sectorNum << gFlash->sector.shift);
while (gFlashNumRemainingBytes > 0)
{
result = ProgramByte(src, dest);
if (result != 0)
break;
gFlashNumRemainingBytes--;
src++;
dest++;
}
return result;
}

View File

@ -382,7 +382,7 @@ void StoreSpriteCallbackInData6(struct Sprite *sprite, SpriteCallback callback)
static void SetCallbackToStoredInData6(struct Sprite *sprite)
{
u32 callback = (u16)sprite->data[6] | (sprite->data[7] << 16);
u32 callback = (u16)sprite->data[6] | ((u16)sprite->data[7] << 16);
sprite->callback = (SpriteCallback)callback;
}

View File

@ -793,7 +793,7 @@ static void AnimShakeMonOrBattleTerrain(struct Sprite *sprite)
StoreSpriteCallbackInData6(sprite, (void *)&gSpriteCoordOffsetY);
break;
}
sprite->data[4] = *(u16 *)(sprite->data[6] | (sprite->data[7] << 16));
sprite->data[4] = *(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]);
sprite->data[5] = gBattleAnimArgs[3];
var0 = sprite->data[5] - 2;
if (var0 < 2)
@ -816,13 +816,13 @@ static void AnimShakeMonOrBattleTerrain_Step(struct Sprite *sprite)
else
{
sprite->data[1] = sprite->data[2];
*(u16 *)(sprite->data[6] | (sprite->data[7] << 16)) += sprite->data[0];
*(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]) += sprite->data[0];
sprite->data[0] = -sprite->data[0];
}
}
else
{
*(u16 *)(sprite->data[6] | (sprite->data[7] << 16)) = sprite->data[4];
*(u16 *)LoadPointerFromVars(sprite->data[6], sprite->data[7]) = sprite->data[4];
var0 = sprite->data[5] - 2;
if (var0 < 2)
for (i = 0; i < gBattlersCount; ++i)

View File

@ -3862,18 +3862,19 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
{
if (!gPaletteFade.active)
{
FreeAllWindowBuffers(); // This needs to be moved up here to avoid a use-after-free
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
FreeMonSpritesGfx();
FreeBattleResources();
FreeBattleSpritesData();
}
ResetSpriteData();
if (gLeveledUpInBattle == 0 || gBattleOutcome != B_OUTCOME_WON)
gBattleMainFunc = ReturnFromBattleToOverworld;
else
gBattleMainFunc = TryEvolvePokemon;
FreeAllWindowBuffers();
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
FreeMonSpritesGfx();
FreeBattleSpritesData();
FreeBattleResources();
}
}
}

View File

@ -3858,7 +3858,7 @@ static void Cmd_playanimation(void)
|| gBattlescriptCurrInstr[2] == B_ANIM_SUBSTITUTE_FADE
|| gBattlescriptCurrInstr[2] == B_ANIM_SILPH_SCOPED)
{
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr);
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 7;
}
@ -3872,7 +3872,7 @@ static void Cmd_playanimation(void)
|| gBattlescriptCurrInstr[2] == B_ANIM_SANDSTORM_CONTINUES
|| gBattlescriptCurrInstr[2] == B_ANIM_HAIL_CONTINUES)
{
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr);
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 7;
}
@ -3882,7 +3882,7 @@ static void Cmd_playanimation(void)
}
else
{
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr);
BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], argumentPtr != NULL ? *argumentPtr : 0);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 7;
}
@ -3902,7 +3902,7 @@ static void Cmd_playanimation_var(void)
|| *animationIdPtr == B_ANIM_SNATCH_MOVE
|| *animationIdPtr == B_ANIM_SUBSTITUTE_FADE)
{
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr);
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 10;
}
@ -3915,7 +3915,7 @@ static void Cmd_playanimation_var(void)
|| *animationIdPtr == B_ANIM_SANDSTORM_CONTINUES
|| *animationIdPtr == B_ANIM_HAIL_CONTINUES)
{
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr);
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 10;
}
@ -3925,7 +3925,7 @@ static void Cmd_playanimation_var(void)
}
else
{
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr);
BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, argumentPtr != NULL ? *argumentPtr : 0);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 10;
}

View File

@ -633,6 +633,7 @@ bool8 IsBattleTransitionDone(void)
InitTransitionData();
FREE_AND_SET_NULL(sTransitionData);
DestroyTask(taskId);
SetHBlankCallback(NULL); //prevents use after free crash in HBlankCB_Phase2_Mugshots
return TRUE;
}
else
@ -692,8 +693,10 @@ static bool8 Transition_StartMain(struct Task *task)
static bool8 Transition_WaitForMain(struct Task *task)
{
task->tTransitionDone = FALSE;
if (FindTaskIdByFunc(sTasks_Main[task->tTransitionId]) == TASK_NONE)
if (FindTaskIdByFunc(sTasks_Main[task->tTransitionId]) == TASK_NONE){
task->tTransitionDone = TRUE;
SetVBlankCallback(NULL); // Fixes use-after-free of sTransitionData in callbacks
}
return FALSE;
}

View File

@ -493,7 +493,7 @@ u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset)
bool8 IsDma3ManagerBusyWithBgCopy(void)
{
int i;
VBlankIntrWait();
for (i = 0; i < 0x80; i++)
{
u8 div = i / 0x20;

View File

@ -141,6 +141,7 @@ static void Task_HandleYesNoMenu(u8 taskId)
AddTextPrinterParameterized4(1, FONT_NORMAL, 0, 3, 1, 1, sTextColor, 0, gText_ClearingData);
CopyWindowToVram(1, COPYWIN_FULL);
ClearSaveData();
Platform_StoreSaveFile();
break;
case MENU_NOTHING_CHOSEN:
default:

View File

@ -1,14 +1,16 @@
#include "global.h"
#include "gba/flash_internal.h"
u8 REG_BASE[0x400];
struct SoundInfo *SOUND_INFO_PTR;
u16 INTR_CHECK;
void *INTR_VECTOR;
u8 PLTT[PLTT_SIZE];
u32 VRAM[VRAM_SIZE/sizeof(u32)];
u8 OAM[OAM_SIZE];
u8 FLASH_BASE[FLASH_ROM_SIZE_1M];
u8 REG_BASE[0x400] __attribute__ ((aligned (4))) = {0};
u8 PLTT[PLTT_SIZE] __attribute__ ((aligned (4))) = {0};
u8 VRAM_[VRAM_SIZE] __attribute__ ((aligned (4))) = {0};
u8 OAM[OAM_SIZE] __attribute__ ((aligned (4))) = {0};
u8 FLASH_BASE[FLASH_ROM_SIZE_1M] __attribute__ ((aligned (4))) = {0};
const s16 sineTable[256] = {
(s16)0x0000, (s16)0x0192, (s16)0x0323, (s16)0x04B5, (s16)0x0645, (s16)0x07D5, (s16)0x0964, (s16)0x0AF1,
@ -451,6 +453,3 @@ u16 Sqrt(u32 num)
return bound;
}
void VBlankIntrWait(void)
{
}

View File

@ -124,73 +124,7 @@ static const char str_checkMbootLL[] = "RFU-MBOOT";
*_dst++ = *_src++; \
} while (0)
u16 rfu_initializeAPI(u32 *APIBuffer, u16 buffByteSize, IntrFunc *sioIntrTable_p, bool8 copyInterruptToRam)
{
u16 i;
u16 *dst;
const u16 *src;
u16 buffByteSizeMax;
// is in EWRAM?
if (((uintptr_t)APIBuffer & 0xF000000) == 0x2000000 && copyInterruptToRam)
return ERR_RFU_API_BUFF_ADR;
// is not 4-byte aligned?
if ((u32)APIBuffer & 3)
return ERR_RFU_API_BUFF_ADR;
if (copyInterruptToRam)
{
// An assert/debug print may have existed before, ie
// printf("%s %u < %u", "somefile.c:12345", buffByteSize, num)
// to push this into buffByteSizeMax?
buffByteSizeMax = RFU_API_BUFF_SIZE_RAM;
if (buffByteSize < buffByteSizeMax)
return ERR_RFU_API_BUFF_SIZE;
}
if (!copyInterruptToRam)
{
buffByteSizeMax = RFU_API_BUFF_SIZE_ROM; // same issue as above
if (buffByteSize < buffByteSizeMax)
return ERR_RFU_API_BUFF_SIZE;
}
gRfuLinkStatus = (void *)APIBuffer + 0;
gRfuStatic = (void *)APIBuffer + 0xb4; // + sizeof(*gRfuLinkStatus)
gRfuFixed = (void *)APIBuffer + 0xdc; // + sizeof(*gRfuStatic)
gRfuSlotStatusNI[0] = (void *)APIBuffer + 0x1bc; // + sizeof(*gRfuFixed)
gRfuSlotStatusUNI[0] = (void *)APIBuffer + 0x37c; // + sizeof(*gRfuSlotStatusNI[0]) * RFU_CHILD_MAX
for (i = 1; i < RFU_CHILD_MAX; ++i)
{
gRfuSlotStatusNI[i] = &gRfuSlotStatusNI[i - 1][1];
gRfuSlotStatusUNI[i] = &gRfuSlotStatusUNI[i - 1][1];
}
// remaining space in API buffer is used for `struct RfuIntrStruct`.
gRfuFixed->STWIBuffer = (struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1];
STWI_init_all((struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1], sioIntrTable_p, copyInterruptToRam);
rfu_STC_clearAPIVariables();
for (i = 0; i < RFU_CHILD_MAX; ++i)
{
gRfuSlotStatusNI[i]->recvBuffer = NULL;
gRfuSlotStatusNI[i]->recvBufferSize = 0;
gRfuSlotStatusUNI[i]->recvBuffer = NULL;
gRfuSlotStatusUNI[i]->recvBufferSize = 0;
}
// rfu_REQ_changeMasterSlave is the function next to rfu_STC_fastCopy
#if LIBRFU_VERSION < 1026
src = (const u16 *)((uintptr_t)&rfu_STC_fastCopy & ~1);
dst = gRfuFixed->fastCopyBuffer;
buffByteSizeMax = ((void *)rfu_REQ_changeMasterSlave - (void *)rfu_STC_fastCopy) / sizeof(u16);
while (buffByteSizeMax-- != 0)
*dst++ = *src++;
#else
COPY(
(uintptr_t)&rfu_STC_fastCopy & ~1,
gRfuFixed->fastCopyBuffer,
buffByteSizeMax,
0x60 / sizeof(u16)
);
#endif
gRfuFixed->fastCopyPtr = (void *)gRfuFixed->fastCopyBuffer + 1;
return 0;
}
static void rfu_STC_clearAPIVariables(void)
{
@ -317,41 +251,6 @@ u16 rfu_getRFUStatus(u8 *rfuState)
return 0;
}
/*
* RFU Multiboot images are loaded into IWRAM
* struct RfuMbootLL
* {
* struct RfuLinkStatus status;
* u8 filler_B4[0x3C];
* char name[10];
* u16 checksum;
* }
* Returns 1 if the packet to inherit is malformed.
*/
u16 rfu_MBOOT_CHILD_inheritanceLinkStatus(void)
{
const char *s1 = str_checkMbootLL;
char *s2 = (char *)0x30000F0;
u16 checksum;
u16 *mb_buff_iwram_p;
u8 i;
// if (strcmp(s1, s2) != 0) return 1;
while (*s1 != '\0')
if (*s1++ != *s2++)
return 1;
mb_buff_iwram_p = (u16 *)0x3000000;
// The size of struct RfuLinkStatus is 180
checksum = 0;
for (i = 0; i < 180/2; ++i)
checksum += *mb_buff_iwram_p++;
if (checksum != *(u16 *)0x30000FA)
return 1;
CpuCopy16((u16 *)0x3000000, gRfuLinkStatus, sizeof(struct RfuLinkStatus));
gRfuStatic->flags |= 0x80; // mboot
return 0;
}
void rfu_REQ_stopMode(void)
{
@ -434,7 +333,7 @@ void rfu_REQ_configSystem(u16 availSlotFlag, u8 maxMFrame, u8 mcTimer)
u16 IMEBackup = REG_IME;
REG_IME = 0;
gRfuStatic->linkEmergencyLimit = Div(600, mcTimer);
gRfuStatic->linkEmergencyLimit = 600 / mcTimer;
REG_IME = IMEBackup;
}
}

View File

@ -242,21 +242,6 @@ static const u8 sLinkErrorTextColor[] = { 0x00, 0x01, 0x02 };
bool8 IsWirelessAdapterConnected(void)
{
if (QL_IS_PLAYBACK_STATE)
return FALSE;
SetWirelessCommType1();
InitRFUAPI();
RfuSetIgnoreError(TRUE);
if (rfu_LMAN_REQBN_softReset_and_checkID() == RFU_ID)
{
rfu_REQ_stopMode();
rfu_waitREQComplete();
return TRUE;
}
SetWirelessCommType0_Internal();
CloseLink();
RestoreSerialTimer3IntrHandlers();
return FALSE;
}
@ -1637,53 +1622,6 @@ void LinkPlayerFromBlock(u32 who)
// When this function returns TRUE the callbacks are skipped
bool8 HandleLinkConnection(void)
{
bool32 main1Failed;
bool32 main2Failed;
if (gWirelessCommType == 0)
{
gLinkStatus = LinkMain1(&gShouldAdvanceLinkState, gSendCmd, gRecvCmds);
LinkMain2(&gMain.heldKeys);
if ((gLinkStatus & LINK_STAT_RECEIVED_NOTHING) && IsSendingKeysOverCable() == TRUE)
return TRUE;
}
else
{
#if REVISION >= 0xA
bool32 reloadOrReset = FALSE;
if (svc_51())
{
// Documentation issue:
// Rfu_IsMaster supposedly returns bool8, but just returns gRfu.ParentChild
// That value has three states, see librfu.h. One of those states is MODE_NEUTRAL (0xFF) which means init.
// So the last condition basically means "rfu link status is connected, not initialising".
// As in, it's true if value is MODE_CHILD or MODE_PARENT but not MODE_NEUTRAL (because unsigned cmp).
if (!FuncIsActiveTask(Task_WirelessCommunicationScreen) && (InUnionRoom() || gReceivedRemoteLinkPlayers != 0 || Rfu_IsMaster() <= MODE_PARENT))
{
reloadOrReset = TRUE;
}
CloseLink();
}
#endif
main1Failed = RfuMain1(); // Always returns FALSE
main2Failed = RfuMain2();
#if REVISION >= 0xA
if (reloadOrReset)
{
// If active task is mystery gift then soft reset, otherwise reload the save.
if (FuncIsActiveTask(Task_MysteryGift)) RfuSoftReset();
else RfuReloadSave();
}
else
#endif
if (IsSendingKeysOverCable() == TRUE)
{
// This will never be reached.
// IsSendingKeysOverCable is always FALSE for wireless communication
if (main1Failed == TRUE || IsRfuRecvQueueEmpty() || main2Failed)
return TRUE;
}
}
return FALSE;
}

View File

@ -315,14 +315,7 @@ void InitRFU(void)
void InitRFUAPI(void)
{
if (!rfu_initializeAPI(sRfuAPIBuffer, RFU_API_BUFF_SIZE_RAM, &gIntrTable[1], TRUE))
{
gLinkType = 0;
// ClearSavedLinkPlayers(); // Em fix
RfuSetIgnoreError(FALSE);
ResetLinkRfuGFLayer();
rfu_setTimerInterrupt(3, &gIntrTable[2]);
}
}
static void Task_ParentSearchForChildren(u8 taskId)

View File

@ -102,18 +102,18 @@ void MoveSaveBlocks_ResetHeap(void)
pokemonStorageCopy = (struct PokemonStorage *)(gHeap + sizeof(struct SaveBlock2) + sizeof(struct SaveBlock1));
// backup the saves.
*saveBlock2Copy = *gSaveBlock2Ptr;
*saveBlock1Copy = *gSaveBlock1Ptr;
*pokemonStorageCopy = *gPokemonStoragePtr;
CpuCopy32(gSaveBlock2Ptr, saveBlock2Copy, sizeof(*gSaveBlock2Ptr));
CpuCopy32(gSaveBlock1Ptr, saveBlock1Copy, sizeof(*gSaveBlock1Ptr));
CpuCopy32(gPokemonStoragePtr, pokemonStorageCopy, sizeof(*gPokemonStoragePtr));
// change saveblocks' pointers
SetSaveBlocksPointers(); // unlike Emerald, this does not use
// the trainer ID sum for an offset.
// restore saveblock data since the pointers changed
*gSaveBlock2Ptr = *saveBlock2Copy;
*gSaveBlock1Ptr = *saveBlock1Copy;
*gPokemonStoragePtr = *pokemonStorageCopy;
CpuCopy32(saveBlock2Copy, gSaveBlock2Ptr, sizeof(*saveBlock2Copy));
CpuCopy32(saveBlock1Copy, gSaveBlock1Ptr, sizeof(*saveBlock1Copy));
CpuCopy32(pokemonStorageCopy, gPokemonStoragePtr, sizeof(*pokemonStorageCopy));
// heap was destroyed in the copying process, so reset it
InitHeap(gHeap, HEAP_SIZE);

View File

@ -1,29 +1,30 @@
#include "global.h"
#include "gba/m4a_internal.h"
#include "cgb_audio.h"
extern const u8 gCgb3Vol[];
#define BSS_CODE __attribute__((section(".bss.code")))
BSS_CODE ALIGNED(4) char SoundMainRAM_Buffer[0x800] = {0};
struct SoundInfo gSoundInfo = {0};
struct PokemonCrySong gPokemonCrySongs[MAX_POKEMON_CRIES] = {0};
struct MusicPlayerInfo gPokemonCryMusicPlayers[MAX_POKEMON_CRIES] = {0};
MPlayFunc gMPlayJumpTable[36] = {0};
struct CgbChannel gCgbChans[4] = {0};
struct MusicPlayerTrack gPokemonCryTracks[MAX_POKEMON_CRIES * 2] = {0};
struct PokemonCrySong gPokemonCrySong = {0};
struct MusicPlayerInfo gMPlayInfo_BGM = {0};
struct MusicPlayerInfo gMPlayInfo_SE1 = {0};
struct MusicPlayerInfo gMPlayInfo_SE2 = {0};
u8 gMPlayMemAccArea[0x10] = {0};
struct MusicPlayerInfo gMPlayInfo_SE3 = {0};
struct SoundInfo gSoundInfo;
struct PokemonCrySong gPokemonCrySongs[MAX_POKEMON_CRIES];
struct MusicPlayerInfo gPokemonCryMusicPlayers[MAX_POKEMON_CRIES];
MPlayFunc gMPlayJumpTable[36];
struct CgbChannel gCgbChans[4];
struct MusicPlayerTrack gPokemonCryTracks[MAX_POKEMON_CRIES * 2];
struct PokemonCrySong gPokemonCrySong;
struct MusicPlayerInfo gMPlayInfo_BGM;
struct MusicPlayerInfo gMPlayInfo_SE1;
struct MusicPlayerInfo gMPlayInfo_SE2;
struct MusicPlayerInfo gMPlayInfo_SE3;
struct MusicPlayerTrack gMPlayTrack_BGM[10];
struct MusicPlayerTrack gMPlayTrack_SE1[3];
struct MusicPlayerTrack gMPlayTrack_SE2[9];
struct MusicPlayerTrack gMPlayTrack_SE3[1];
u8 gMPlayMemAccArea[0x10];
void MP2K_event_nxx();
void MP2KPlayerMain();
u32 MidiKeyToFreq(struct WaveData *wav, u8 key, u8 fineAdjust)
{
@ -76,12 +77,10 @@ void m4aSoundInit(void)
{
s32 i;
CpuCopy32((void *)((s32)SoundMainRAM & ~1), SoundMainRAM_Buffer, sizeof(SoundMainRAM_Buffer));
SoundInit(&gSoundInfo);
MPlayExtender(gCgbChans);
m4aSoundMode(SOUND_MODE_DA_BIT_8
| SOUND_MODE_FREQ_13379
| SOUND_MODE_FREQ_42048
| (12 << SOUND_MODE_MASVOL_SHIFT)
| (5 << SOUND_MODE_MAXCHN_SHIFT));
@ -106,7 +105,7 @@ void m4aSoundInit(void)
void m4aSoundMain(void)
{
SoundMain();
//RunMixerFrame();
}
void m4aSongNumStart(u16 n)
@ -279,6 +278,11 @@ void MPlayExtender(struct CgbChannel *cgbChans)
REG_NR30 = 0;
REG_NR50 = 0x77;
for(u8 i = 0; i < 4; i++){
cgb_set_envelope(i, 8);
cgb_trigger_note(i);
}
soundInfo = SOUND_INFO_PTR;
ident = soundInfo->ident;
@ -290,10 +294,10 @@ void MPlayExtender(struct CgbChannel *cgbChans)
#if __STDC_VERSION__ < 202311L
gMPlayJumpTable[8] = ply_memacc;
gMPlayJumpTable[17] = ply_lfos;
gMPlayJumpTable[19] = ply_mod;
gMPlayJumpTable[17] = MP2K_event_lfos;
gMPlayJumpTable[19] = MP2K_event_mod;
gMPlayJumpTable[28] = ply_xcmd;
gMPlayJumpTable[29] = ply_endtie;
gMPlayJumpTable[29] = MP2K_event_endtie;
gMPlayJumpTable[30] = SampleFreqSet;
gMPlayJumpTable[31] = TrackStop;
gMPlayJumpTable[32] = FadeOutBody;
@ -386,7 +390,7 @@ void SoundInit(struct SoundInfo *soundInfo)
soundInfo->maxChans = 8;
soundInfo->masterVolume = 15;
soundInfo->plynote = ply_note;
soundInfo->plynote = MP2K_event_nxx;
soundInfo->CgbSound = DummyFunc;
soundInfo->CgbOscOff = (CgbOscOffFunc)DummyFunc;
soundInfo->MidiKeyToCgbFreq = (MidiKeyToCgbFreqFunc)DummyFunc;
@ -396,7 +400,7 @@ void SoundInit(struct SoundInfo *soundInfo)
soundInfo->MPlayJumpTable = gMPlayJumpTable;
SampleFreqSet(SOUND_MODE_FREQ_13379);
SampleFreqSet(SOUND_MODE_FREQ_42048);
soundInfo->ident = ID_NUMBER;
}
@ -407,14 +411,12 @@ void SampleFreqSet(u32 freq)
freq = (freq & 0xF0000) >> 16;
soundInfo->freq = freq;
soundInfo->pcmSamplesPerVBlank = gPcmSamplesPerVBlankTable[freq - 1];
soundInfo->pcmSamplesPerVBlank = 701;
soundInfo->pcmDmaPeriod = PCM_DMA_BUF_SIZE / soundInfo->pcmSamplesPerVBlank;
// LCD refresh rate 59.7275Hz
soundInfo->pcmFreq = (597275 * soundInfo->pcmSamplesPerVBlank + 5000) / 10000;
soundInfo->pcmFreq = 60.0f * soundInfo->pcmSamplesPerVBlank;
// CPU frequency 16.78Mhz
soundInfo->divFreq = (16777216 / soundInfo->pcmFreq + 1) >> 1;
soundInfo->divFreq = 1.0f / soundInfo->pcmFreq;
// Turn off timer 0.
REG_TM0CNT_H = 0;
@ -424,12 +426,6 @@ void SampleFreqSet(u32 freq)
m4aSoundVSyncOn();
while (*(vu8 *)REG_ADDR_VCOUNT == 159)
;
while (*(vu8 *)REG_ADDR_VCOUNT != 159)
;
REG_TM0CNT_H = TIMER_ENABLE | TIMER_1CLK;
}
@ -606,8 +602,8 @@ void MPlayOpen(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track
soundInfo->MPlayMainHead = NULL;
}
soundInfo->musicPlayerHead = mplayInfo;
soundInfo->MPlayMainHead = MPlayMain;
soundInfo->musicPlayerHead = (u32)mplayInfo;
soundInfo->MPlayMainHead = (u32)MP2KPlayerMain;
soundInfo->ident = ID_NUMBER;
mplayInfo->ident = ID_NUMBER;
}
@ -877,6 +873,8 @@ void CgbOscOff(u8 chanNum)
REG_NR42 = 8;
REG_NR44 = 0x80;
}
cgb_set_envelope(chanNum - 1, 8);
cgb_trigger_note(chanNum - 1);
}
static inline int CgbPan(struct CgbChannel *chan)
@ -1000,6 +998,7 @@ void CgbSound(void)
{
case 1:
*nrx0ptr = channels->sweep;
cgb_set_sweep(channels->sweep);
// fallthrough
case 2:
*nrx1ptr = ((u32)channels->wavePointer << 6) + channels->length;
@ -1013,6 +1012,7 @@ void CgbSound(void)
REG_WAVE_RAM2 = channels->wavePointer[2];
REG_WAVE_RAM3 = channels->wavePointer[3];
channels->currentPointer = channels->wavePointer;
cgb_set_wavram();
}
*nrx0ptr = 0;
*nrx1ptr = channels->length;
@ -1032,6 +1032,7 @@ void CgbSound(void)
channels->n4 = 0x00;
break;
}
cgb_set_length(ch - 1, channels->length);
channels->envelopeCounter = channels->attack;
if ((s8)(channels->attack & mask))
{
@ -1228,6 +1229,9 @@ void CgbSound(void)
if (ch == 1 && !(*nrx0ptr & 0x08))
*nrx4ptr = channels->n4 | 0x80;
}
cgb_set_envelope(ch - 1, *nrx2ptr);
cgb_toggle_length(ch - 1, (*nrx4ptr & 0x40));
cgb_trigger_note(ch - 1);
}
channel_complete:

View File

@ -1,13 +1,12 @@
#include "global.h"
//stubs
void umul3232H32(){}
void SoundMain(){}
void SoundMainRAM(){}
void SoundMainBTM(){}
void RealClearChain(){}
void ply_fine(){}
void MPlayJumpTableCopy(){}
void ply_goto(){}
void ply_patt(){}
void ply_pend(){}
@ -24,10 +23,9 @@ void ply_lfodl(){}
void ply_modt(){}
void ply_tune(){}
void ply_port(){}
void m4aSoundVSync(){}
void MPlayMain(){}
void TrackStop(){}
void ChnVolSetAsm(){}
void ply_note(){}
void ply_endtie(){}
void clear_modM(){}

View File

@ -5,36 +5,36 @@
// for now.
void *const gMPlayJumpTableTemplate[] =
{
ply_fine,
ply_goto,
ply_patt,
ply_pend,
ply_rept,
ply_fine,
ply_fine,
ply_fine,
ply_fine,
ply_prio,
ply_tempo,
ply_keysh,
ply_voice,
ply_vol,
ply_pan,
ply_bend,
ply_bendr,
ply_lfos,
ply_lfodl,
ply_mod,
ply_modt,
ply_fine,
ply_fine,
ply_tune,
ply_fine,
ply_fine,
ply_fine,
ply_port,
ply_fine,
ply_endtie,
MP2K_event_fine,
MP2K_event_goto,
MP2K_event_patt,
MP2K_event_pend,
MP2K_event_rept,
MP2K_event_fine,
MP2K_event_fine,
MP2K_event_fine,
MP2K_event_fine,
MP2K_event_prio,
MP2K_event_tempo,
MP2K_event_keysh,
MP2K_event_voice,
MP2K_event_vol,
MP2K_event_pan,
MP2K_event_bend,
MP2K_event_bendr,
MP2K_event_lfos,
MP2K_event_lfodl,
MP2K_event_mod,
MP2K_event_modt,
MP2K_event_fine,
MP2K_event_fine,
MP2K_event_tune,
MP2K_event_fine,
MP2K_event_fine,
MP2K_event_fine,
MP2K_event_port,
MP2K_event_fine,
MP2K_event_endtie,
SampleFreqSet,
TrackStop,
FadeOutBody,

View File

@ -99,12 +99,6 @@ void EnableVCountIntrAtLine150(void);
#define B_START_SELECT (B_BUTTON | START_BUTTON | SELECT_BUTTON)
int main(void)
{
AgbMain();
return 0;
}
void AgbMain()
{
#if REVISION >= 0xA
@ -122,7 +116,7 @@ void AgbMain()
InitIntrHandlers();
m4aSoundInit();
EnableVCountIntrAtLine150();
InitRFU();
//InitRFU();
CheckForFlashMemory();
InitMainCallbacks();
InitMapMusic();
@ -136,23 +130,14 @@ void AgbMain()
SetNotInSaveFailedScreen();
// Revision 10 has no calls into libisagbprn except this one.
#if !defined(NDEBUG) || REVISION >= 0xA
#if (LOG_HANDLER == LOG_HANDLER_MGBA_PRINT)
(void) MgbaOpen();
#elif (LOG_HANDLER == LOG_HANDLER_AGB_PRINT)
AGBPrintInit();
#endif
#endif
#if REVISION >= 1
if (gFlashMemoryPresent != TRUE)
SetMainCallback2(NULL);
#endif
gLinkTransferringData = FALSE;
for (;;)
int i=0;
for (;;i++)
{
ReadKeys();
@ -190,6 +175,7 @@ void AgbMain()
PlayTimeCounter_Update();
MapMusicMain();
WaitForVBlank();
}
}
@ -272,7 +258,7 @@ void InitKeys(void)
static void ReadKeys(void)
{
u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
u16 keyInput = Platform_GetKeyInput();
gMain.newKeysRaw = keyInput & ~gMain.heldKeysRaw;
gMain.newKeys = gMain.newKeysRaw;
gMain.newAndRepeatedKeys = gMain.newKeysRaw;
@ -363,13 +349,11 @@ static void VBlankIntr(void)
RfuVSync();
else if (!gLinkVSyncDisabled)
LinkVSync();
if (gMain.vblankCounter1)
(*gMain.vblankCounter1)++;
if (gMain.vblankCallback)
gMain.vblankCallback();
gMain.vblankCounter2++;
CopyBufferedValuesToGpuRegs();
@ -380,11 +364,13 @@ static void VBlankIntr(void)
#if !defined(NDEBUG) || REVISION >= 0xA
sVcountBeforeSound = REG_VCOUNT;
#endif
m4aSoundMain();
#if !defined(NDEBUG) || REVISION >= 0xA
sVcountAfterSound = REG_VCOUNT;
#endif
#endif
TryReceiveLinkBattleData();
Random();
UpdateWirelessStatusIndicatorSprite();
@ -438,10 +424,7 @@ static void IntrDummy(void)
static void WaitForVBlank(void)
{
gMain.intrCheck &= ~INTR_FLAG_VBLANK;
while (!(gMain.intrCheck & INTR_FLAG_VBLANK))
;
VBlankIntrWait();
}
void SetVBlankCounter1Ptr(u32 *ptr)

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "malloc.h"
u8 gHeap[HEAP_SIZE] = {0};
ALIGNED(4) u8 gHeap[HEAP_SIZE] = {0};
static void *sHeapStart;
static u32 sHeapSize;

View File

@ -2,403 +2,8 @@
#include "multiboot.h"
static u16 MultiBoot_required_data[MULTIBOOT_NCHILD];
static int MultiBootSend(struct MultiBootParam *mp, u16 data);
static int MultiBootHandShake(struct MultiBootParam *mp);
static void MultiBootWaitCycles(u32 cycles);
static void MultiBootWaitSendDone(void);
void MultiBootInit(struct MultiBootParam *mp)
{
mp->client_bit = 0;
mp->probe_count = 0;
mp->response_bit = 0;
mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT;
mp->sendflag = 0;
mp->handshake_timeout = 0;
REG_RCNT = 0;
REG_SIOCNT = SIO_MULTI_MODE | SIO_115200_BPS;
REG_SIODATA8 = 0;
}
int MultiBootMain(struct MultiBootParam *mp)
{
int i, j, k;
if (MultiBootCheckComplete(mp))
{
return 0;
}
if (mp->check_wait > MULTIBOOT_CONNECTION_CHECK_WAIT)
{
mp->check_wait--;
return 0;
}
output_burst:
if (mp->sendflag)
{
mp->sendflag = 0;
i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_ERROR | SIO_ID | SIO_MULTI_SD | SIO_MULTI_SI);
if (i != SIO_MULTI_SD)
{
MultiBootInit(mp);
return i ^ SIO_MULTI_SD;
}
}
if (mp->probe_count >= 0xe0)
{
i = MultiBootHandShake(mp);
if (i)
{
return i;
}
if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK
&& mp->probe_count > 0xe1
&& MultiBootCheckComplete(mp) == 0)
{
MultiBootWaitSendDone();
goto output_burst;
}
if (MultiBootCheckComplete(mp) == 0)
{
if (mp->handshake_timeout == 0)
{
MultiBootInit(mp);
return MULTIBOOT_ERROR_HANDSHAKE_FAILURE;
}
mp->handshake_timeout--;
}
return 0;
}
switch (mp->probe_count)
{
case 0:
k = 0x0e;
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
if (*(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2) != 0xffff)
{
break;
}
k >>= 1;
}
k &= 0x0e;
mp->response_bit = k;
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
if (mp->client_bit & (1 << i))
{
if (j != ((MULTIBOOT_CLIENT_INFO << 8) | (1 << i)))
{
k = 0;
break;
}
}
}
mp->client_bit &= k;
if (k == 0)
{
mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT;
}
if (mp->check_wait)
{
mp->check_wait--;
}
else
{
if (mp->response_bit != mp->client_bit)
{
MultiBootStartProbe(mp);
goto case_1;
}
}
output_master_info:
return MultiBootSend(mp, (MULTIBOOT_MASTER_INFO << 8) | mp->client_bit);
case_1:
case 1:
mp->probe_target_bit = 0;
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
if ((j >> 8) == MULTIBOOT_CLIENT_INFO)
{
MultiBoot_required_data[i - 1] = j;
j &= 0xff;
if (j == (1 << i))
{
mp->probe_target_bit |= j;
}
}
}
if (mp->response_bit != mp->probe_target_bit)
{
goto output_master_info;
}
mp->probe_count = 2;
return MultiBootSend(mp, (MULTIBOOT_MASTER_START_PROBE << 8) | mp->probe_target_bit);
case 2:
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
if (mp->probe_target_bit & (1 << i))
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
if (j != MultiBoot_required_data[i - 1])
{
mp->probe_target_bit ^= 1 << i;
}
}
}
goto output_header;
case 0xd0:
k = 1;
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
mp->client_data[i - 1] = j;
if (mp->probe_target_bit & (1 << i))
{
if ((j >> 8) != MULTIBOOT_CLIENT_INFO
&& (j >> 8) != MULTIBOOT_CLIENT_DLREADY)
{
MultiBootInit(mp);
return MULTIBOOT_ERROR_NO_DLREADY;
}
if (j == MultiBoot_required_data[i - 1])
{
k = 0;
}
}
}
if (k == 0)
{
return MultiBootSend(mp, (MULTIBOOT_MASTER_REQUEST_DLREADY << 8) | mp->palette_data);
}
mp->probe_count = 0xd1;
k = 0x11;
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
k += mp->client_data[i - 1];
}
mp->handshake_data = k;
return MultiBootSend(mp, (MULTIBOOT_MASTER_START_DL << 8) | (k & 0xff));
case 0xd1:
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
if (mp->probe_target_bit & (1 << i))
{
if ((j >> 8) != MULTIBOOT_CLIENT_DLREADY)
{
MultiBootInit(mp);
return MULTIBOOT_ERROR_NO_DLREADY;
}
}
}
i = MultiBoot(mp);
if (i == 0)
{
mp->probe_count = 0xe0;
mp->handshake_timeout = MULTIBOOT_HANDSHAKE_TIMEOUT;
return 0;
}
MultiBootInit(mp);
mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT * 2;
return MULTIBOOT_ERROR_BOOT_FAILURE;
default:
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
if (mp->probe_target_bit & (1 << i))
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
if ((j >> 8) != (MULTIBOOT_MASTER_START_PROBE + 1 - (mp->probe_count >> 1))
|| ((j & 0xff) != (1 << i)))
{
mp->probe_target_bit ^= 1 << i;
}
}
}
if (mp->probe_count == 0xc4)
{
mp->client_bit = mp->probe_target_bit & 0x0e;
mp->probe_count = 0;
goto output_master_info;
}
output_header:
if (mp->probe_target_bit == 0)
{
MultiBootInit(mp);
return MULTIBOOT_ERROR_NO_PROBE_TARGET;
}
mp->probe_count += 2;
if (mp->probe_count == 0xc4)
{
goto output_master_info;
}
i = MultiBootSend(mp,
(mp->masterp[mp->probe_count - 4 + 1] << 8)
| mp->masterp[mp->probe_count - 4]);
if (i)
{
return i;
}
if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK)
{
MultiBootWaitSendDone();
goto output_burst;
}
return 0;
}
}
static int MultiBootSend(struct MultiBootParam *mp, u16 data)
{
int i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_MULTI_SD | SIO_MULTI_SI);
if (i != SIO_MULTI_SD)
{
MultiBootInit(mp);
return i ^ SIO_MULTI_SD;
}
REG_SIODATA8 = data;
REG_SIOCNT = SIO_MULTI_MODE | SIO_START | SIO_115200_BPS;
mp->sendflag = 1;
return 0;
}
void MultiBootStartProbe(struct MultiBootParam *mp)
{
if (mp->probe_count != 0)
{
MultiBootInit(mp);
return;
}
mp->check_wait = 0;
mp->client_bit = 0;
mp->probe_count = 1;
}
void MultiBootStartMaster(struct MultiBootParam *mp, const u8 *srcp, int length, u8 palette_color, s8 palette_speed)
{
int i = 0;
if (mp->probe_count != 0
|| mp->client_bit == 0
|| mp->check_wait != 0)
{
MultiBootInit(mp);
return;
}
mp->boot_srcp = srcp;
length = (length + 15) & ~15;
if (length < MULTIBOOT_SEND_SIZE_MIN || length > MULTIBOOT_SEND_SIZE_MAX)
{
MultiBootInit(mp);
return;
}
mp->boot_endp = srcp + length;
switch (palette_speed)
{
case -4:
case -3:
case -2:
case -1:
i = (palette_color << 3) | (3 - palette_speed);
break;
case 0:
i = 0x38 | palette_color;
break;
case 1:
case 2:
case 3:
case 4:
i = (palette_color << 3) | (palette_speed - 1);
break;
}
mp->palette_data = ((i & 0x3f) << 1) | 0x81;
mp->probe_count = 0xd0;
}
bool32 MultiBootCheckComplete(struct MultiBootParam *mp)
{
if (mp->probe_count == 0xe9)
return 1;
else
return 0;
}
static int MultiBootHandShake(struct MultiBootParam *mp)
{
int i, j;
#define send_data (mp->system_work[0])
#define must_data (mp->system_work[1])
switch (mp->probe_count)
{
case_0xe0:
case 0xe0:
mp->probe_count = 0xe1;
must_data = 0x0000;
send_data = 0x100000;
return MultiBootSend(mp, 0x0000);
default:
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
if ((mp->client_bit & (1 << i))
&& j != must_data)
{
goto case_0xe0;
}
}
mp->probe_count++;
must_data = send_data & 0xffff;
if (send_data == 0x0000)
{
must_data = mp->masterp[0xac] | (mp->masterp[0xad] << 8);
send_data = must_data << 5;
}
send_data >>= 5;
output_common:
return MultiBootSend(mp, send_data);
case 0xe7:
case 0xe8:
for (i = MULTIBOOT_NCHILD; i != 0; i--)
{
j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
if ((mp->client_bit & (1 << i)) && j != must_data)
{
MultiBootInit(mp);
return MULTIBOOT_ERROR_HANDSHAKE_FAILURE;
}
}
mp->probe_count++;
if (mp->probe_count == 0xe9)
{
return 0;
}
send_data = mp->masterp[0xae] | (mp->masterp[0xaf] << 8);
must_data = send_data;
goto output_common;
}
#undef send_data
#undef must_data
}
static NOINLINE void MultiBootWaitCycles(u32 cycles)
{
}
static void MultiBootWaitSendDone(void)
{
int i;
for (i = 0; (i < 31069) && (REG_SIOCNT & SIO_START); i++);
MultiBootWaitCycles(600);
}
void MultiBootInit(struct MultiBootParam *mp){}
int MultiBootMain(struct MultiBootParam *mp){}
void MultiBootStartProbe(struct MultiBootParam *mp){}
void MultiBootStartMaster(struct MultiBootParam *mp, const u8 *srcp, int length, u8 palette_color, s8 palette_speed){}
bool32 MultiBootCheckComplete(struct MultiBootParam *mp){}

806
src/music_player.c Normal file
View File

@ -0,0 +1,806 @@
#include "mp2k_common.h"
#include "music_player.h"
#include "gba/types.h"
#include "gba/m4a_internal.h"
#include "platform.h"
// Don't uncomment this. vvvvv
// #define POKEMON_EXTENSIONS
#define MIXED_AUDIO_BUFFER_SIZE 4907
static u32 MidiKeyToFreq(struct WaveData2 *wav, u8 key, u8 pitch);
extern void * const gMPlayJumpTableTemplate[];
extern const u8 gScaleTable[];
extern const u32 gFreqTable[];
extern const u8 gClockTable[];
float audioBuffer [MIXED_AUDIO_BUFFER_SIZE];
u32 umul3232H32(u32 a, u32 b) {
u64 result = a;
result *= b;
return result >> 32;
}
void SoundMainBTM(void *ptr)
{
CpuFill32(0, ptr, 0x40);
}
// Removes chan from the doubly-linked list of channels associated with chan->track.
// Gonna rename this to like "FreeChannel" or something, similar to VGMS
void MP2KClearChain(struct MixerSource *chan) {
struct MP2KTrack *track = chan->track;
if (chan->track == NULL) {
return;
}
struct MixerSource *next = chan->next;
struct MixerSource *prev = chan->prev;
if (prev != NULL) {
prev->next = next;
} else {
track->chan = next;
}
if (next != NULL) {
next->prev = prev;
}
chan->track = NULL;
}
// In case newer compilers are too dumb to remove this logic at compile time.
#define SKIP_GBA_BIOS_CHECKS
// #define NOT_GBA
#if defined(SKIP_GBA_BIOS_CHECKS) || defined(NOT_GBA)
#define VERIFY_PTR(x) do; while (0)
#else
#define VERIFY_PTR(x) do {\
uintptr_t y = (uintptr_t)(x);\
if (y < EWRAM_START && (y < (uintptr_t)&gMPlayJumpTableTemplate || y >= 0x40000)) {\
ret = 0;\
}\
} while (0)
#endif
static u8 SafeDereferenceU8(u8 *addr) {
u8 ret = *addr;
VERIFY_PTR(addr);
return ret;
}
static u32 SafeDereferenceU32(u32 *addr) {
u32 ret = *addr;
VERIFY_PTR(addr);
return ret;
}
static u8 *SafeDereferenceU8Ptr(u8 **addr) {
u8 *ret = *addr;
VERIFY_PTR(addr);
return ret;
}
static u32 *SafeDereferenceU32Ptr(u32 **addr) {
u32 *ret = *addr;
VERIFY_PTR(addr);
return ret;
}
static struct WaveData2 *SafeDereferenceWavDataPtr(struct WaveData2 **addr) {
struct WaveData2 *ret = *addr;
VERIFY_PTR(addr);
return ret;
}
static struct MP2KInstrument *SafeDereferenceMP2KInstrumentPtr(struct MP2KInstrument **addr) {
struct MP2KInstrument *ret = *addr;
VERIFY_PTR(addr);
return ret;
}
static void *SafeDereferenceVoidPtr(void **addr) {
void *ret = *addr;
VERIFY_PTR(addr);
return ret;
}
#ifndef NOT_GBA
// I read an article about undefined behavior today so I'm feeling especially cautious
// I'm definitely bringing this onto compiler explorer for some laughs... heh...
// ...Sorry
struct MP2KInstrument SafeDereferenceMP2KInstrument(struct MP2KInstrument *addr) {
struct MP2KInstrument instrument;
if (addr->type == SafeDereferenceU8(&addr->type)
&& addr->drumKey == SafeDereferenceU8(&addr->drumKey)
&& addr->cgbLength == SafeDereferenceU8(&addr->cgbLength)
&& addr->panSweep == SafeDereferenceU8(&addr->panSweep)) {
instrument.type = addr->type;
instrument.drumKey = addr->drumKey;
instrument.cgbLength = addr->cgbLength;
instrument.panSweep = addr->panSweep;
} else {
instrument.type = 0;
instrument.drumKey = 0;
instrument.cgbLength = 0;
instrument.panSweep = 0;
}
// I don't know how much the optimizer can eff with weird union stuff so I might as well go through
// all the steps to check which union member to access...?
if (instrument.type & 0xC0) {
if (addr->group == SafeDereferenceMP2KInstrumentPtr(&addr->group)) {
instrument.group = addr->group;
} else {
instrument.group = NULL;
}
if (addr->keySplitTable == SafeDereferenceU8Ptr(&addr->keySplitTable)) {
instrument.keySplitTable = addr->keySplitTable;
} else {
instrument.keySplitTable = NULL;
}
return instrument;
} else if (instrument.type & 0x7) {
if ((instrument.type & 0x7) == 3) {
if (addr->cgb3Sample == SafeDereferenceU32Ptr(&addr->cgb3Sample)) {
instrument.cgb3Sample = addr->cgb3Sample;
} else {
instrument.cgb3Sample = NULL;
}
} else {
if (addr->squareNoiseConfig == SafeDereferenceU32(&addr->squareNoiseConfig)) {
instrument.squareNoiseConfig = addr->squareNoiseConfig;
} else {
instrument.squareNoiseConfig = 0;
}
}
} else {
if (addr->wav == SafeDereferenceWavDataPtr(&addr->wav)) {
instrument.wav = addr->wav;
} else {
instrument.wav = NULL;
}
}
if (addr->attack == SafeDereferenceU8(&addr->attack)
&& addr->decay == SafeDereferenceU8(&addr->decay)
&& addr->sustain == SafeDereferenceU8(&addr->sustain)
&& addr->release == SafeDereferenceU8(&addr->release)) {
instrument.attack = addr->attack;
instrument.decay = addr->decay;
instrument.sustain = addr->sustain;
instrument.release = addr->release;
} else {
instrument.attack = 0;
instrument.decay = 0;
instrument.sustain = 0;
instrument.release = 0;
}
return instrument;
}
#endif
#undef VERIFY_PTR
u8 ConsumeTrackByte(struct MP2KTrack *track) {
u8 *ptr = track->cmdPtr++;
return SafeDereferenceU8(ptr);
}
void MPlayJumpTableCopy(void **mplayJumpTable) {
for (uf8 i = 0; i < 36; i++) {
mplayJumpTable[i] = SafeDereferenceVoidPtr(&gMPlayJumpTableTemplate[i]);
}
}
// Ends the current track. (Fine as in the Italian musical word, not English)
void MP2K_event_fine(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
struct MP2KTrack *r5 = track;
for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) {
if (chan->status & 0xC7) {
chan->status |= 0x40;
}
MP2KClearChain(chan);
}
track->status = 0;
}
// Sets the track's cmdPtr to the specified address.
void MP2K_event_goto(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
#ifdef NOT_GBA
u8 *addr;
memcpy(&addr, track->cmdPtr, sizeof(u8 *));
track->cmdPtr = addr;
#else
u8 *cmdPtr = track->cmdPtr;
uintptr_t addr = 0;
for (size_t i = sizeof(uintptr_t) - 1; i > 0; i--) {
addr |= cmdPtr[i];
addr <<= 8;
}
addr |= SafeDereferenceU8(cmdPtr);
track->cmdPtr = (u8*)addr;
#endif
}
// Sets the track's cmdPtr to the specified address after backing up its current position.
void MP2K_event_patt(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
u8 level = track->patternLevel;
if (level < 3) {
track->patternStack[level] = track->cmdPtr + sizeof(u8 *);
track->patternLevel++;
MP2K_event_goto(unused, track);
} else {
// Stop playing this track, as an indication to the music programmer that they need to quit
// nesting patterns so darn much.
MP2K_event_fine(unused, track);
}
}
// Marks the end of the current pattern, if there is one, by resetting the pattern to the
// most recently saved value.
void MP2K_event_pend(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
if (track->patternLevel != 0) {
u8 index = --track->patternLevel;
track->cmdPtr = track->patternStack[index];
}
}
// Loops back until a REPT event has been reached the specified number of times
void MP2K_event_rept(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
if (*track->cmdPtr == 0) {
// "Repeat 0 times" == loop forever
track->cmdPtr++;
MP2K_event_goto(unused, track);
} else {
u8 repeatCount = ++track->repeatCount;
if (repeatCount < ConsumeTrackByte(track)) {
MP2K_event_goto(unused, track);
} else {
track->repeatCount = 0;
track->cmdPtr += sizeof(u8) + sizeof(u8 *);
}
}
}
// Sets the note priority for new notes in this track.
void MP2K_event_prio(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->priority = ConsumeTrackByte(track);
}
// Sets the BPM of all tracks to the specified tempo (in beats per half-minute, because 255 as a max tempo
// kinda sucks but 510 is plenty).
void MP2K_event_tempo(struct MP2KPlayerState *player, struct MP2KTrack *track) {
u16 bpm = ConsumeTrackByte(track);
bpm *= 2;
player->tempoRawBPM = bpm;
player->tempoInterval = (bpm * player->tempoScale) / 256;
}
void MP2K_event_keysh(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->keyShift = ConsumeTrackByte(track);
track->status |= 0xC;
}
void MP2K_event_voice(struct MP2KPlayerState *player, struct MP2KTrack *track) {
u8 voice = *(track->cmdPtr++);
struct MP2KInstrument *instrument = &player->voicegroup[voice];
#ifdef NOT_GBA
track->instrument = *instrument;
#else
track->instrument = SafeDereferenceMP2KInstrument(instrument);
#endif
}
void MP2K_event_vol(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->vol = ConsumeTrackByte(track);
track->status |= 0x3;
}
void MP2K_event_pan(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->pan = ConsumeTrackByte(track) - 0x40;
track->status |= 0x3;
}
void MP2K_event_bend(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->bend = ConsumeTrackByte(track) - 0x40;
track->status |= 0xC;
}
void MP2K_event_bendr(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->bendRange = ConsumeTrackByte(track);
track->status |= 0xC;
}
void MP2K_event_lfodl(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->lfoDelay = ConsumeTrackByte(track);
}
void MP2K_event_modt(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
u8 type = ConsumeTrackByte(track);
if (type != track->modType) {
track->modType = type;
track->status |= 0xF;
}
}
void MP2K_event_tune(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->tune = ConsumeTrackByte(track) - 0x40;
track->status |= 0xC;
}
void MP2K_event_port(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
// I'm really curious whether any games actually use this event...
#ifdef NOT_GBA
// I assume anything done by this command will get immediately overwritten by CgbSound?
track->cmdPtr += 2;
#else
vu8* offset = (vu8 *)(REG_ADDR_NR10 + *(track->cmdPtr++));
*offset = ConsumeTrackByte(track);
#endif
}
void MP2KPlayerMain(void *voidPtrPlayer) {
struct MP2KPlayerState *player = (struct MP2KPlayerState *)voidPtrPlayer;
struct SoundMixerState *mixer = SOUND_INFO_PTR;
if (player->lockStatus != PLAYER_UNLOCKED) {
return;
}
player->lockStatus = PLAYER_LOCKED;
if (player->nextPlayerFunc != NULL) {
player->nextPlayerFunc(player->nextPlayer);
}
if (player->status & MUSICPLAYER_STATUS_PAUSE) {
goto returnEarly;
}
FadeOutBody(voidPtrPlayer);
if (player->status & MUSICPLAYER_STATUS_PAUSE) {
goto returnEarly;
}
player->tempoCounter += player->tempoInterval;
while (player->tempoCounter >= 150) {
u16 trackBits = 0;
for (u32 i = 0; i < player->trackCount; i++) {
struct MP2KTrack *currentTrack = player->tracks + i;
struct MixerSource *chan;
if ((currentTrack->status & MPT_FLG_EXIST) == 0) {
continue;
}
trackBits |= (1 << i);
chan = currentTrack->chan;
while (chan != NULL) {
if(chan->gateTime == 0) {
//chan = chan->next;
chan->status |= SOUND_CHANNEL_SF_STOP;
break;
}
if ((chan->status & SOUND_CHANNEL_SF_ON) == 0) {
ClearChain(chan);
} else if (chan->gateTime != 0 && --chan->gateTime == 0) {
chan->status |= SOUND_CHANNEL_SF_STOP;
}
chan = chan->next;
}
if (currentTrack->status & MPT_FLG_START) {
CpuFill32(0, currentTrack, 0x40);
currentTrack->status = MPT_FLG_EXIST;
currentTrack->bendRange = 2;
currentTrack->volPublic = 64;
currentTrack->lfoSpeed = 22;
currentTrack->instrument.type = 1;
}
while (currentTrack->wait == 0) {
u8 event = *currentTrack->cmdPtr;
if (event < 0x80) {
event = currentTrack->runningStatus;
} else {
currentTrack->cmdPtr++;
if (event >= 0xBD) {
currentTrack->runningStatus = event;
}
}
if (event >= 0xCF) {
mixer->mp2kEventNxxFunc(event - 0xCF, player, currentTrack);
} else if (event >= 0xB1) {
void (*eventFunc)(struct MP2KPlayerState *, struct MP2KTrack *);
player->cmd = event - 0xB1;
eventFunc = mixer->mp2kEventFuncTable[player->cmd];
eventFunc(player, currentTrack);
if (currentTrack->status == 0) {
goto nextTrack;
}
} else {
currentTrack->wait = gClockTable[event - 0x80];
}
}
currentTrack->wait--;
if (currentTrack->lfoSpeed != 0 && currentTrack->modDepth != 0) {
if (currentTrack->lfoDelayCounter != 0U) {
currentTrack->lfoDelayCounter--;
goto nextTrack;
}
currentTrack->lfoSpeedCounter += currentTrack->lfoSpeed;
s8 r;
if (currentTrack->lfoSpeedCounter >= 0x40U && currentTrack->lfoSpeedCounter < 0xC0U) {
r = 128 - currentTrack->lfoSpeedCounter;
} else if (currentTrack->lfoSpeedCounter >= 0xC0U) {
// Unsigned -> signed casts where the value is out of range are implementation defined.
// Why not add a few extra lines to make behavior the same for literally everyone?
r = currentTrack->lfoSpeedCounter - 256;
} else {
r = currentTrack->lfoSpeedCounter;
}
r = FLOOR_DIV_POW2(currentTrack->modDepth * r, 64);
if (r != currentTrack->modCalculated) {
currentTrack->modCalculated = r;
if (currentTrack->modType == 0) {
currentTrack->status |= MPT_FLG_PITCHG;
} else {
currentTrack->status |= MPT_FLG_VOLCHG;
}
}
}
nextTrack:;
}
player->clock++;
if (trackBits == 0) {
player->status = MUSICPLAYER_STATUS_PAUSE;
goto returnEarly;
}
player->status = trackBits;
player->tempoCounter -= 150;
}
u32 i = 0;
do {
printf( "probleman2\n");
struct MP2KTrack *track = player->tracks + i;
if ((track->status & MPT_FLG_EXIST) == 0 || (track->status & 0xF) == 0) {
continue;
}
TrkVolPitSet(player, track);
for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) {
printf( "probleman3 %d\n",chan->status);
if(chan->status & SOUND_CHANNEL_SF_STOP){ break;}
if ((chan->status & 0xC7) == 0) {
ClearChain(chan);
continue;
}
u8 cgbType = chan->type & 0x7;
if (track->status & MPT_FLG_VOLCHG) {
ChnVolSetAsm(chan, track);
if (cgbType != 0) {
chan->cgbStatus |= 1;
}
}
if (track->status & MPT_FLG_PITCHG) {
s32 key = chan->key + track->keyShiftCalculated;
if (key < 0) {
key = 0;
}
if (cgbType != 0) {
chan->freq = mixer->cgbCalcFreqFunc(cgbType, key, track->pitchCalculated);
chan->cgbStatus |= 0x2;
} else {
chan->freq = MidiKeyToFreq(chan->wav, key, track->pitchCalculated);
}
}
}
track->status &= ~0xF;
}
while(++i < player->trackCount);
printf("problema3\n");
returnEarly: ;
player->lockStatus = PLAYER_UNLOCKED;
}
void TrackStop(struct MP2KPlayerState *player, struct MP2KTrack *track) {
if (track->status & 0x80) {
for (struct MixerSource *chan = track->chan; chan != NULL; chan = chan->next) {
if (chan->status != 0) {
u8 cgbType = chan->type & 0x7;
if (cgbType != 0) {
struct SoundMixerState *mixer = SOUND_INFO_PTR;
mixer->cgbNoteOffFunc(cgbType);
}
chan->status = 0;
}
chan->track = NULL;
}
track->chan = NULL;
}
}
void ChnVolSetAsm(struct MixerSource *chan, struct MP2KTrack *track) {
s8 forcedPan = chan->rhythmPan;
u32 rightVolume = (u8)(forcedPan + 128) * chan->velocity * track->volRightCalculated / 128 / 128;
if (rightVolume > 0xFF) {
rightVolume = 0xFF;
}
chan->rightVol = rightVolume;
u32 leftVolume = (u8)(127 - forcedPan) * chan->velocity * track->volLeftCalculated / 128 / 128;
if (leftVolume > 0xFF) {
leftVolume = 0xFF;
}
chan->leftVol = leftVolume;
}
void MP2K_event_nxx(u8 clock, struct MP2KPlayerState *player, struct MP2KTrack *track) { // ply_note
struct SoundMixerState *mixer = SOUND_INFO_PTR;
// A note can be anywhere from 1 to 4 bytes long. First is always the note length...
track->gateTime = gClockTable[clock];
if (*track->cmdPtr < 0x80) {
// Then the note name...
track->key = *(track->cmdPtr++);
if (*track->cmdPtr < 0x80) {
// Then the velocity...
track->velocity = *(track->cmdPtr++);
if (*track->cmdPtr < 0x80) {
// Then a number to add ticks to get exotic or more precise note lengths without TIE.
track->gateTime += *(track->cmdPtr++);
}
}
}
// sp14
s8 forcedPan = 0;
// First r4, then r9
struct MP2KInstrument *instrument = &track->instrument;
// sp8
u8 key = track->key;
u8 type = instrument->type;
if (type & (TONEDATA_TYPE_RHY | TONEDATA_TYPE_SPL)) {
u8 instrumentIndex;
if (instrument->type & TONEDATA_TYPE_SPL) {
instrumentIndex = instrument->keySplitTable[track->key];
} else {
instrumentIndex = track->key;
}
instrument = instrument->group + instrumentIndex;
if (instrument->type & (TONEDATA_TYPE_RHY | TONEDATA_TYPE_SPL)) {
return;
}
if (type & TONEDATA_TYPE_RHY) {
if (instrument->panSweep & 0x80) {
forcedPan = ((s8)(instrument->panSweep & 0x7F) - 0x40) * 2;
}
key = instrument->drumKey;
}
}
// sp10
uf16 priority = player->priority + track->priority;
if (priority > 0xFF) {
priority = 0xFF;
}
u8 cgbType = instrument->type & TONEDATA_TYPE_CGB;
struct MixerSource *chan;
if (cgbType != 0) {
if (mixer->cgbChans == NULL) {
return;
}
// There's only one CgbChannel of a given type, so we don't need to loop to find it.
chan = mixer->cgbChans + cgbType - 1;
// If this channel is running and not stopped,
if ((chan->status & SOUND_CHANNEL_SF_ON)
&& (chan->status & SOUND_CHANNEL_SF_STOP) == 0) {
// then make sure this note is higher priority (or same priority but from a later track).
if (chan->priority > priority || (chan->priority == priority && chan->track < track)) {
return;
}
}
} else {
uf16 p = priority;
struct MP2KTrack *t = track;
bool32 foundStoppingChannel = FALSE;
chan = NULL;
u8 maxChans = mixer->numChans;
struct MixerSource *currChan = mixer->chans;
for (uf8 i = 0; i < maxChans; i++, currChan++) {
if ((currChan->status & SOUND_CHANNEL_SF_ON) == 0) {
// Hey, we found a completely inactive channel! Let's use that.
chan = currChan;
break;
}
if (currChan->status & SOUND_CHANNEL_SF_STOP && !foundStoppingChannel) {
// In the absence of a completely finalized channel, we can take over one that's about to
// finalize. That's a tier above any channel that's currently playing a note.
foundStoppingChannel = TRUE;
p = currChan->priority;
t = currChan->track;
chan = currChan;
} else if ((currChan->status & SOUND_CHANNEL_SF_STOP && foundStoppingChannel)
|| ((currChan->status & SOUND_CHANNEL_SF_STOP) == 0 && !foundStoppingChannel)) {
// The channel we're checking is on the same tier, so check the priority and track order
if (currChan->priority < p) {
p = currChan->priority;
t = currChan->track;
chan = currChan;
} else if (currChan->priority == p && currChan->track > t) {
t = currChan->track;
chan = currChan;
} else if (currChan->priority == p && currChan->track == t) {
chan = currChan;
}
}
}
}
if (chan == NULL) {
return;
}
ClearChain(chan);
chan->prev = NULL;
chan->next = track->chan;
if (track->chan != NULL) {
track->chan->prev = chan;
}
track->chan = chan;
chan->track = track;
track->lfoDelayCounter = track->lfoDelay;
if (track->lfoDelay != 0) {
ClearModM(track);
}
TrkVolPitSet(player, track);
chan->gateTime = track->gateTime;
chan->untransposedKey = track->key;
chan->velocity = track->velocity;
chan->priority = priority;
chan->key = key;
chan->rhythmPan = forcedPan;
chan->type = instrument->type;
chan->wav = instrument->wav;
chan->attack = instrument->attack;
chan->decay = instrument->decay;
chan->sustain = instrument->sustain;
chan->release = instrument->release;
chan->echoVol = track->echoVolume;
chan->echoLen = track->echoLength;
ChnVolSetAsm(chan, track);
// Avoid promoting keyShiftCalculated to u8 by splitting the addition into a separate statement
sf16 transposedKey = chan->key;
transposedKey += track->keyShiftCalculated;
if (transposedKey < 0) {
transposedKey = 0;
}
if (cgbType != 0) {
//struct CgbChannel *cgbChan = (struct CgbChannel *)chan;
chan->length = instrument->cgbLength;
if (instrument->panSweep & 0x80 || (instrument->panSweep & 0x70) == 0) {
chan->sweep = 8;
} else {
chan->sweep = instrument->panSweep;
}
chan->freq = mixer->cgbCalcFreqFunc(cgbType, transposedKey, track->pitchCalculated);
} else {
#ifdef POKEMON_EXTENSIONS
chan->ct = track->ct;
#endif
chan->freq = MidiKeyToFreq(chan->wav, transposedKey, track->pitchCalculated);
}
chan->status = SOUND_CHANNEL_SF_START;
track->status &= ~0xF;
}
void MP2K_event_endtie(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
u8 key = *track->cmdPtr;
if (key < 0x80) {
track->key = key;
track->cmdPtr++;
} else {
key = track->key;
}
struct MixerSource *chan = track->chan;
while (chan != NULL) {
if (chan->status & 0x83 && (chan->status & 0x40) == 0 && chan->untransposedKey == key) {
chan->status |= 0x40;
return;
}
chan = chan->next;
}
}
void MP2K_event_lfos(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->lfoSpeed = *(track->cmdPtr++);
if (track->lfoSpeed == 0) {
ClearModM(track);
}
}
void MP2K_event_mod(struct MP2KPlayerState *unused, struct MP2KTrack *track) {
track->modDepth = *(track->cmdPtr++);
if (track->modDepth == 0) {
ClearModM(track);
}
}
void m4aSoundVSync(void)
{
struct SoundMixerState *mixer = SOUND_INFO_PTR;
if(mixer->lockStatus-PLAYER_UNLOCKED <= 1)
{
s32 samplesPerFrame = mixer->samplesPerFrame * 2;
float *m4aBuffer = mixer->outBuffer;
float *cgbBuffer = cgb_get_buffer();
s32 dmaCounter = mixer->dmaCounter;
if (dmaCounter > 1) {
m4aBuffer += samplesPerFrame * (mixer->framesPerDmaCycle - (dmaCounter - 1));
}
for(u32 i = 0; i < samplesPerFrame; i++)
audioBuffer[i] = m4aBuffer[i] + cgbBuffer[i];
Platform_QueueAudio(audioBuffer, samplesPerFrame * 4);
if((s8)(--mixer->dmaCounter) <= 0)
mixer->dmaCounter = mixer->framesPerDmaCycle;
}
}
#if 0
// In:
// - wav: pointer to sample
// - key: the note after being transposed. If pitch bend puts it between notes, then the note below.
// - pitch: how many 256ths of a semitone above `key` the current note is.
// Out:
// - The frequency in Hz at which the sample should be played back.
u32 MidiKeyToFreq(struct WaveData2 *wav, u8 key, u8 pitch) {
if (key > 178) {
key = 178;
pitch = 255;
}
// Alternatively, note = key % 12 and octave = 14 - (key / 12)
u8 note = gScaleTable[key] & 0xF;
u8 octave = gScaleTable[key] >> 4;
u8 nextNote = gScaleTable[key + 1] & 0xF;
u8 nextOctave = gScaleTable[key + 1] >> 4;
u32 baseFreq1 = gFreqTable[note] >> octave;
u32 baseFreq2 = gFreqTable[nextNote] >> nextOctave;
u32 freqDifference = umul3232H32(baseFreq2 - baseFreq1, pitch << 24);
// This is added by me. The real GBA and GBA BIOS don't verify this address, and as a result the
// BIOS's memory can be dumped.
u32 freq = SafeDereferenceU32(&wav->freq);
return umul3232H32(freq, baseFreq1 + freqDifference);
}
#endif

View File

@ -336,38 +336,7 @@ bool32 MysteryGift_TrySaveStamp(const u16 * stamp)
void MysteryGift_LoadLinkGameData(struct MysteryGiftLinkGameData * data)
{
s32 i;
CpuFill32(0, data, sizeof(*data));
// Magic
data->unk_00 = GAME_DATA_VALID_VAR;
data->unk_04 = 1;
data->unk_08 = 1;
data->unk_0C = 1;
data->unk_10 = VERSION_CODE;
// Check whether a card already exists
if (ValidateSavedWonderCard())
{
// Populate fields
data->flagId = GetSavedWonderCard()->flagId;
data->cardMetadata = *GetSavedWonderCardMetadata();
data->maxStamps = GetSavedWonderCard()->maxStamps;
}
else
{
data->flagId = 0;
}
for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++)
data->questionnaireWords[i] = gSaveBlock1Ptr->mysteryGift.questionnaireWords[i];
CopyTrainerId(data->playerTrainerId, gSaveBlock2Ptr->playerTrainerId);
StringCopy(data->playerName, gSaveBlock2Ptr->playerName);
for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++)
data->easyChatProfile[i] = gSaveBlock1Ptr->easyChatProfile[i];
memcpy(data->gameCode, RomHeaderGameCode, GAME_CODE_LENGTH);
data->version = RomHeaderSoftwareVersion;
}
bool32 MysteryGift_ValidateLinkGameData(const struct MysteryGiftLinkGameData * data)

View File

@ -718,10 +718,11 @@ static bool8 MainState_Exit(void)
{
if (!gPaletteFade.active)
{
if (sNamingScreen->templateNum == NAMING_SCREEN_PLAYER)
SeedRngAndSetTrainerId();
SetMainCallback2(sNamingScreen->returnCallback);
DestroyTask(FindTaskIdByFunc(Task_NamingScreen));
ResetVHBlank();
// Fix use-after-free issues with gNamingScreenData caused by sprites calling their callbacks which attempt to read from gNamingScreenData.
ResetSpriteData();
FreeAllWindowBuffers();
FREE_AND_SET_NULL(sNamingScreen);
RestoreHelpContext();

View File

@ -47,13 +47,16 @@ void SetTrainerId(u32 trainerId, u8 *dst)
void CopyTrainerId(u8 *dst, u8 *src)
{
s32 i;
for (i = 0; i < 4; i++)
for (i = 0; i < 4; i++){
if (src == NULL)
dst[i] = 0;
dst[i] = src[i];
}
}
static void InitPlayerTrainerId(void)
{
u32 trainerId = (Random() << 0x10) | GetGeneratedTrainerIdLower();
u32 trainerId = (Random() << 16 | Random());
SetTrainerId(trainerId, gSaveBlock2Ptr->playerTrainerId);
}

View File

@ -1966,7 +1966,6 @@ static void DestroyPikachuOrPlatformSprites(u8 taskId, u8 spriteType)
static void LoadTrainerPic(u16 whichPic, u16 tileOffset)
{
u32 i;
switch (whichPic)
{
case MALE_PLAYER_PIC:
@ -1988,7 +1987,6 @@ static void LoadTrainerPic(u16 whichPic, u16 tileOffset)
default:
return;
}
sOakSpeechResources->trainerPicTilemap = AllocZeroed(0x60);
for (i = 0; i < 0x60; i++)
((u8 *)sOakSpeechResources->trainerPicTilemap)[i] = i;

257
src/platform/cgb_audio.c Normal file
View File

@ -0,0 +1,257 @@
#include "global.h"
#include "cgb_audio.h"
#include "cgb_tables.h"
struct AudioCGB gb;
float soundChannelPos[4];
const s16 *PU1Table;
const s16 *PU2Table;
u32 apuFrame;
u8 apuCycle;
u32 sampleRate;
u16 lfsrMax[2];
float ch4Samples;
void cgb_audio_init(u32 rate){
gb.ch1Freq = 0;
gb.ch1SweepCounter = 0;
gb.ch1SweepCounterI = 0;
gb.ch1SweepDir = 0;
gb.ch1SweepShift = 0;
for (u8 ch = 0; ch < 4; ch++){
gb.Vol[ch] = 0;
gb.VolI[ch] = 0;
gb.Len[ch] = 0;
gb.LenI[ch] = 0;
gb.LenOn[ch] = 0;
gb.EnvCounter[ch] = 0;
gb.EnvCounterI[ch] = 0;
gb.EnvDir[ch] = 0;
gb.DAC[ch] = 0;
soundChannelPos[ch] = 0;
}
soundChannelPos[1] = 1;
PU1Table = PU0;
PU2Table = PU0;
sampleRate = rate;
gb.ch4LFSR[0] = 0x8000;
gb.ch4LFSR[1] = 0x80;
lfsrMax[0] = 0x8000;
lfsrMax[1] = 0x80;
ch4Samples = 0.0f;
}
void cgb_set_sweep(u8 sweep){
gb.ch1SweepDir = (sweep & 0x08) >> 3;
gb.ch1SweepCounter = gb.ch1SweepCounterI = (sweep & 0x70) >> 4;
gb.ch1SweepShift = (sweep & 0x07);
}
void cgb_set_wavram(){
for(u8 wavi = 0; wavi < 0x10; wavi++){
gb.WAVRAM[(wavi << 1)] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0xF0) >> 4) / 7.5f - 1.0f;
gb.WAVRAM[(wavi << 1) + 1] = (((*(REG_ADDR_WAVE_RAM0 + wavi)) & 0x0F)) / 7.5f - 1.0f;
}
}
void cgb_toggle_length(u8 channel, bool8 state){
gb.LenOn[channel] = state;
}
void cgb_set_length(u8 channel, u8 length){
gb.Len[channel] = gb.LenI[channel] = length;
}
void cgb_set_envelope(u8 channel, u8 envelope){
if(channel == 2){
switch((envelope & 0xE0)){
case 0x00: // mute
gb.Vol[2] = gb.VolI[2] = 0;
break;
case 0x20: // full
gb.Vol[2] = gb.VolI[2] = 4;
break;
case 0x40: // half
gb.Vol[2] = gb.VolI[2] = 2;
break;
case 0x60: // quarter
gb.Vol[2] = gb.VolI[2] = 1;
break;
case 0x80: // 3 quarters
gb.Vol[2] = gb.VolI[2] = 3;
break;
}
}else{
gb.DAC[channel] = (envelope & 0xF8) > 0;
gb.Vol[channel] = gb.VolI[channel] = (envelope & 0xF0) >> 4;
gb.EnvDir[channel] = (envelope & 0x08) >> 3;
gb.EnvCounter[channel] = gb.EnvCounterI[channel] = (envelope & 0x07);
}
}
void cgb_trigger_note(u8 channel){
gb.Vol[channel] = gb.VolI[channel];
gb.Len[channel] = gb.LenI[channel];
if(channel != 2) gb.EnvCounter[channel] = gb.EnvCounterI[channel];
if(channel == 3) {
gb.ch4LFSR[0] = 0x8000;
gb.ch4LFSR[1] = 0x80;
}
}
void cgb_audio_generate(u16 samplesPerFrame){
float *outBuffer = gb.outBuffer;
switch(REG_NR11 & 0xC0){
case 0x00:
PU1Table = PU0;
break;
case 0x40:
PU1Table = PU1;
break;
case 0x80:
PU1Table = PU2;
break;
case 0xC0:
PU1Table = PU3;
break;
}
switch(REG_NR21 & 0xC0){
case 0x00:
PU2Table = PU0;
break;
case 0x40:
PU2Table = PU1;
break;
case 0x80:
PU2Table = PU2;
break;
case 0xC0:
PU2Table = PU3;
break;
}
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
apuFrame += 512;
if(apuFrame >= sampleRate){
apuFrame -= sampleRate;
apuCycle++;
if((apuCycle & 1) == 0){ // Length
for(u8 ch = 0; ch < 4; ch++){
if(gb.Len[ch]){
if(--gb.Len[ch] == 0 && gb.LenOn[ch]){
REG_NR52 &= (0xFF ^ (1 << ch));
}
}
}
}
if((apuCycle & 7) == 7){ // Envelope
for(u8 ch = 0; ch < 4; ch++){
if(ch == 2) continue; // Skip wave channel
if(gb.EnvCounter[ch]){
if(--gb.EnvCounter[ch] == 0){
if(gb.Vol[ch] && !gb.EnvDir[ch]){
gb.Vol[ch]--;
gb.EnvCounter[ch] = gb.EnvCounterI[ch];
}else if(gb.Vol[ch] < 0x0F && gb.EnvDir[ch]){
gb.Vol[ch]++;
gb.EnvCounter[ch] = gb.EnvCounterI[ch];
}
}
}
}
}
if((apuCycle & 3) == 2){ // Sweep
if(gb.ch1SweepCounterI && gb.ch1SweepShift){
if(--gb.ch1SweepCounter == 0){
gb.ch1Freq = REG_SOUND1CNT_X & 0x7FF;
if(gb.ch1SweepDir){
gb.ch1Freq -= gb.ch1Freq >> gb.ch1SweepShift;
if(gb.ch1Freq & 0xF800) gb.ch1Freq = 0;
}else{
gb.ch1Freq += gb.ch1Freq >> gb.ch1SweepShift;
if(gb.ch1Freq & 0xF800){
gb.ch1Freq = 0;
gb.EnvCounter[0] = 0;
gb.Vol[0] = 0;
}
}
REG_NR13 = gb.ch1Freq & 0xFF;
REG_NR14 &= 0xF8;
REG_NR14 += (gb.ch1Freq >> 8) & 0x07;
gb.ch1SweepCounter = gb.ch1SweepCounterI;
}
}
}
}
//Sound generation loop
soundChannelPos[0] += freqTable[REG_SOUND1CNT_X & 0x7FF] / (sampleRate / 32);
soundChannelPos[1] += freqTable[REG_SOUND2CNT_H & 0x7FF] / (sampleRate / 32);
soundChannelPos[2] += freqTable[REG_SOUND3CNT_X & 0x7FF] / (sampleRate / 32);
while(soundChannelPos[0] >= 32) soundChannelPos[0] -= 32;
while(soundChannelPos[1] >= 32) soundChannelPos[1] -= 32;
while(soundChannelPos[2] >= 32) soundChannelPos[2] -= 32;
float outputL = 0;
float outputR = 0;
if(REG_NR52 & 0x80){
if((gb.DAC[0]) && (REG_NR52 & 0x01)){
if(REG_NR51 & 0x10) outputL += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f;
if(REG_NR51 & 0x01) outputR += gb.Vol[0] * PU1Table[(int)(soundChannelPos[0])] / 15.0f;
}
if((gb.DAC[1]) && (REG_NR52 & 0x02)){
if(REG_NR51 & 0x20) outputL += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f;
if(REG_NR51 & 0x02) outputR += gb.Vol[1] * PU2Table[(int)(soundChannelPos[1])] / 15.0f;
}
if((REG_NR30 & 0x80) && (REG_NR52 & 0x04)){
if(REG_NR51 & 0x40) outputL += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f;
if(REG_NR51 & 0x04) outputR += gb.Vol[2] * gb.WAVRAM[(int)(soundChannelPos[2])] / 4.0f;
}
if((gb.DAC[3]) && (REG_NR52 & 0x08)){
bool32 lfsrMode = ((REG_NR43 & 0x08) == 8);
ch4Samples += freqTableNSE[REG_SOUND4CNT_H & 0xFF] / sampleRate;
int ch4Out = 0;
if(gb.ch4LFSR[lfsrMode] & 1){
ch4Out++;
}else{
ch4Out--;
}
int avgDiv = 1;
while(ch4Samples >= 1){
avgDiv++;
bool8 lfsrCarry = 0;
if(gb.ch4LFSR[lfsrMode] & 2) lfsrCarry ^= 1;
gb.ch4LFSR[lfsrMode] >>= 1;
if(gb.ch4LFSR[lfsrMode] & 2) lfsrCarry ^= 1;
if(lfsrCarry) gb.ch4LFSR[lfsrMode] |= lfsrMax[lfsrMode];
if(gb.ch4LFSR[lfsrMode] & 1){
ch4Out++;
}else{
ch4Out--;
}
ch4Samples--;
}
float sample = ch4Out;
if(avgDiv > 1) sample /= avgDiv;
if(REG_NR51 & 0x80) outputL += gb.Vol[3] * sample / 15.0f;
if(REG_NR51 & 0x08) outputR += gb.Vol[3] * sample / 15.0f;
}
}
outBuffer[0] = outputL / 4.0f;
outBuffer[1] = outputR / 4.0f;
}
}
float *cgb_get_buffer(){
return gb.outBuffer;
}

110
src/platform/dma.c Normal file
View File

@ -0,0 +1,110 @@
#include "global.h"
#include "platform/dma.h"
struct DMATransfer {
union {
const void *src;
const u16 *src16;
const u32 *src32;
};
union {
void *dst;
vu16 *dst16;
vu32 *dst32;
};
u32 size;
u16 control;
} DMAList[DMA_COUNT];
void RunDMAs(u32 type)
{
for (int dmaNum = 0; dmaNum < DMA_COUNT; dmaNum++)
{
struct DMATransfer *dma = &DMAList[dmaNum];
u32 dmaCntReg = (&REG_DMA0CNT)[dmaNum * 3];
if (!((dmaCntReg >> 16) & DMA_ENABLE))
{
dma->control &= ~DMA_ENABLE;
}
if ( (dma->control & DMA_ENABLE) &&
(((dma->control & DMA_START_MASK) >> 12) == type))
{
//printf("DMA%d src=%p, dest=%p, control=%d\n", dmaNum, dma->src, dma->dest, dma->control);
for (int i = 0; i < (dma->size); i++)
{
if ((dma->control) & DMA_32BIT)
*dma->dst32 = *dma->src32;
else *dma->dst16 = *dma->src16;
// process destination pointer changes
if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_INC)
{
if ((dma->control) & DMA_32BIT)
dma->dst32++;
else dma->dst16++;
}
else if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_DEC)
{
if ((dma->control) & DMA_32BIT)
dma->dst32--;
else dma->dst16--;
}
else if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_RELOAD) // TODO
{
if ((dma->control) & DMA_32BIT)
dma->dst32++;
else dma->dst16++;
}
// process source pointer changes
if (((dma->control) & DMA_SRC_MASK) == DMA_SRC_INC)
{
if ((dma->control) & DMA_32BIT)
dma->src32++;
else dma->src16++;
}
else if (((dma->control) & DMA_SRC_MASK) == DMA_SRC_DEC)
{
if ((dma->control) & DMA_32BIT)
dma->src32--;
else dma->src16--;
}
}
if (dma->control & DMA_REPEAT)
{
dma->size = ((&REG_DMA0CNT)[dmaNum * 3] & 0x1FFFF);
if (((dma->control) & DMA_DEST_MASK) == DMA_DEST_RELOAD)
{
dma->dst = ((&REG_DMA0DAD)[dmaNum * 3]);
}
}
else
{
dma->control &= ~DMA_ENABLE;
}
}
}
}
void DmaSet(int dmaNum, const void *src, void *dest, u32 control)
{
if (dmaNum >= DMA_COUNT)
{
printf("DmaSet with invalid DMA number: dmaNum=%d, src=%p, dest=%p, control=%d\n", dmaNum, src, dest, control);
return;
}
(&REG_DMA0SAD)[dmaNum * 3] = src;
(&REG_DMA0DAD)[dmaNum * 3] = dest;
(&REG_DMA0CNT)[dmaNum * 3] = control;
struct DMATransfer *dma = &DMAList[dmaNum];
dma->src = src;
dma->dst = dest;
dma->size = control & 0x1ffff;
dma->control = control >> 16;
RunDMAs(DMA_NOW);
}

View File

@ -0,0 +1,946 @@
#include "global.h"
#include <stdbool.h>
#include "platform/dma.h"
#define mosaicBGEffectX (REG_MOSAIC & 0xF)
#define mosaicBGEffectY ((REG_MOSAIC >> 4) & 0xF)
#define mosaicSpriteEffectX ((REG_MOSAIC >> 8) & 0xF)
#define mosaicSpriteEffectY ((REG_MOSAIC >> 12) & 0xF)
#define applyBGHorizontalMosaicEffect(x) (x - (x % (mosaicBGEffectX+1)))
#define applyBGVerticalMosaicEffect(y) (y - (y % (mosaicBGEffectY+1)))
#define applySpriteHorizontalMosaicEffect(x) (x - (x % (mosaicSpriteEffectX+1)))
#define applySpriteVerticalMosaicEffect(y) (y - (y % (mosaicSpriteEffectY+1)))
#define getAlphaBit(x) ((x >> 15) & 1)
#define getRedChannel(x) ((x >> 0) & 0x1F)
#define getGreenChannel(x) ((x >> 5) & 0x1F)
#define getBlueChannel(x) ((x >> 10) & 0x1F)
#define isbgEnabled(x) ((REG_DISPCNT >> 8) & 0xF) & (1 << x)
#define WINMASK_BG0 (1 << 0)
#define WINMASK_BG1 (1 << 1)
#define WINMASK_BG2 (1 << 2)
#define WINMASK_BG3 (1 << 3)
#define WINMASK_OBJ (1 << 4)
#define WINMASK_CLR (1 << 5)
#define WINMASK_WINOUT (1 << 6)
extern IntrFunc gIntrTable[];
struct scanlineData {
uint16_t layers[4][DISPLAY_WIDTH];
uint16_t spriteLayers[4][DISPLAY_WIDTH];
uint16_t bgcnts[4];
uint16_t winMask[DISPLAY_WIDTH];
//priority bookkeeping
char bgtoprio[4]; //background to priority
char prioritySortedBgs[4][4];
char prioritySortedBgsCount[4];
};
struct bgPriority {
char priority;
char subPriority;
};
static const uint16_t bgMapSizes[][2] =
{
{32, 32},
{64, 32},
{32, 64},
{64, 64},
};
static void RenderBGScanline(int bgNum, uint16_t control, uint16_t hoffs, uint16_t voffs, int lineNum, uint16_t *line)
{
unsigned int charBaseBlock = (control >> 2) & 3;
unsigned int screenBaseBlock = (control >> 8) & 0x1F;
unsigned int bitsPerPixel = ((control >> 7) & 1) ? 8 : 4;
unsigned int mapWidth = bgMapSizes[control >> 14][0];
unsigned int mapHeight = bgMapSizes[control >> 14][1];
unsigned int mapWidthInPixels = mapWidth * 8;
unsigned int mapHeightInPixels = mapHeight * 8;
uint8_t *bgtiles = (uint8_t *)BG_CHAR_ADDR(charBaseBlock);
uint16_t *pal = (uint16_t *)PLTT;
if (control & BGCNT_MOSAIC)
lineNum = applyBGVerticalMosaicEffect(lineNum);
hoffs &= 0x1FF;
voffs &= 0x1FF;
for (unsigned int x = 0; x < DISPLAY_WIDTH; x++)
{
uint16_t *bgmap = (uint16_t *)BG_SCREEN_ADDR(screenBaseBlock);
// adjust for scroll
unsigned int xx;
if (control & BGCNT_MOSAIC)
xx = (applyBGHorizontalMosaicEffect(x) + hoffs) & 0x1FF;
else
xx = (x + hoffs) & 0x1FF;
unsigned int yy = (lineNum + voffs) & 0x1FF;
//if x or y go above 255 pixels it goes to the next screen base which are 0x400 WORDs long
if (xx > 255 && mapWidthInPixels > 256) {
bgmap += 0x400;
}
if (yy > 255 && mapHeightInPixels > 256) {
//the width check is for 512x512 mode support, it jumps by two screen bases instead
bgmap += (mapWidthInPixels > 256) ? 0x800 : 0x400;
}
//maximum width for bgtile block is 256
xx &= 0xFF;
yy &= 0xFF;
unsigned int mapX = xx / 8;
unsigned int mapY = yy / 8;
uint16_t entry = bgmap[mapY * 32 + mapX];
unsigned int tileNum = entry & 0x3FF;
unsigned int paletteNum = (entry >> 12) & 0xF;
unsigned int tileX = xx % 8;
unsigned int tileY = yy % 8;
// Flip if necessary
if (entry & (1 << 10))
tileX = 7 - tileX;
if (entry & (1 << 11))
tileY = 7 - tileY;
uint16_t tileLoc = tileNum * (bitsPerPixel * 8);
uint16_t tileLocY = tileY * bitsPerPixel;
uint16_t tileLocX = tileX;
if (bitsPerPixel == 4)
tileLocX /= 2;
uint8_t pixel = bgtiles[tileLoc + tileLocY + tileLocX];
if (bitsPerPixel == 4) {
if (tileX & 1)
pixel >>= 4;
else
pixel &= 0xF;
if (pixel != 0)
line[x] = pal[16 * paletteNum + pixel] | 0x8000;
else
line[x] = 0;
}
else {
if (pixel != 0)
line[x] = pal[pixel] | 0x8000;
else
line[x] = 0;
}
}
}
static inline uint32_t getBgX(int bgNumber)
{
if (bgNumber == 2)
{
return REG_BG2X;
}
else if (bgNumber == 3)
{
return REG_BG3X;
}
}
static inline uint32_t getBgY(int bgNumber)
{
if (bgNumber == 2)
{
return REG_BG2Y;
}
else if (bgNumber == 3)
{
return REG_BG3Y;
}
}
static inline uint16_t getBgPA(int bgNumber)
{
if (bgNumber == 2)
{
return REG_BG2PA;
}
else if (bgNumber == 3)
{
return REG_BG3PA;
}
}
static inline uint16_t getBgPB(int bgNumber)
{
if (bgNumber == 2)
{
return REG_BG2PB;
}
else if (bgNumber == 3)
{
return REG_BG3PB;
}
}
static inline uint16_t getBgPC(int bgNumber)
{
if (bgNumber == 2)
{
return REG_BG2PC;
}
else if (bgNumber == 3)
{
return REG_BG3PC;
}
}
static inline uint16_t getBgPD(int bgNumber)
{
if (bgNumber == 2)
{
return REG_BG2PD;
}
else if (bgNumber == 3)
{
return REG_BG3PD;
}
}
static void RenderRotScaleBGScanline(int bgNum, uint16_t control, uint16_t x, uint16_t y, int lineNum, uint16_t *line)
{
vBgCnt *bgcnt = (vBgCnt *)&control;
unsigned int charBaseBlock = bgcnt->charBaseBlock;
unsigned int screenBaseBlock = bgcnt->screenBaseBlock;
unsigned int mapWidth = 1 << (4 + (bgcnt->screenSize)); // number of tiles
uint8_t *bgtiles = (uint8_t *)(VRAM_ + charBaseBlock * 0x4000);
uint8_t *bgmap = (uint8_t *)(VRAM_ + screenBaseBlock * 0x800);
uint16_t *pal = (uint16_t *)PLTT;
if (control & BGCNT_MOSAIC)
lineNum = applyBGVerticalMosaicEffect(lineNum);
s16 pa = getBgPA(bgNum);
s16 pb = getBgPB(bgNum);
s16 pc = getBgPC(bgNum);
s16 pd = getBgPD(bgNum);
if (pa == 0 && pb == 0 && pc == 0 && pd == 0) {
pa = 0x0100;
pd = 0x0100;
}
int sizeX = 128;
int sizeY = 128;
switch (bgcnt->screenSize)
{
case 0:
break;
case 1:
sizeX = sizeY = 256;
break;
case 2:
sizeX = sizeY = 512;
break;
case 3:
sizeX = sizeY = 1024;
break;
}
int maskX = sizeX - 1;
int maskY = sizeY - 1;
int yshift = ((control >> 14) & 3) + 4;
/*int dx = pa & 0x7FFF;
if (pa & 0x8000)
dx |= 0xFFFF8000;
int dmx = pb & 0x7FFF;
if (pb & 0x8000)
dmx |= 0xFFFF8000;
int dy = pc & 0x7FFF;
if (pc & 0x8000)
dy |= 0xFFFF8000;
int dmy = pd & 0x7FFF;
if (pd & 0x8000)
dmy |= 0xFFFF8000;*/
s32 currentX = getBgX(bgNum);
s32 currentY = getBgY(bgNum);
//sign extend 28 bit number
currentX = ((currentX & (1 << 27)) ? currentX | 0xF0000000 : currentX);
currentY = ((currentY & (1 << 27)) ? currentY | 0xF0000000 : currentY);
currentX += lineNum * pb;
currentY += lineNum * pd;
int realX = currentX;
int realY = currentY;
if (bgcnt->areaOverflowMode)
{
for (int x = 0; x < DISPLAY_WIDTH; x++)
{
int xxx = (realX >> 8) & maskX;
int yyy = (realY >> 8) & maskY;
int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)];
int tileX = xxx & 7;
int tileY = yyy & 7;
uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX];
if (pixel != 0) {
line[x] = pal[pixel] | 0x8000;
}
realX += pa;
realY += pc;
}
}
else
{
for (int x = 0; x < DISPLAY_WIDTH; x++)
{
int xxx = (realX >> 8);
int yyy = (realY >> 8);
if (xxx < 0 || yyy < 0 || xxx >= sizeX || yyy >= sizeY)
{
//line[x] = 0x80000000;
}
else
{
int tile = bgmap[(xxx >> 3) + ((yyy >> 3) << yshift)];
int tileX = xxx & 7;
int tileY = yyy & 7;
uint8_t pixel = bgtiles[(tile << 6) + (tileY << 3) + tileX];
if (pixel != 0) {
line[x] = pal[pixel] | 0x8000;
}
}
realX += pa;
realY += pc;
}
}
//the only way i could figure out how to get accurate mosaic on affine bgs
//luckily i dont think pokemon emerald uses mosaic on affine bgs
if (control & BGCNT_MOSAIC && mosaicBGEffectX > 0)
{
for (int x = 0; x < DISPLAY_WIDTH; x++)
{
uint16_t color = line[applyBGHorizontalMosaicEffect(x)];
line[x] = color;
}
}
}
const u8 spriteSizes[][2] =
{
{8, 16},
{8, 32},
{16, 32},
{32, 64},
};
static uint16_t alphaBlendColor(uint16_t targetA, uint16_t targetB)
{
unsigned int eva = REG_BLDALPHA & 0x1F;
unsigned int evb = (REG_BLDALPHA >> 8) & 0x1F;
// shift right by 4 = division by 16
unsigned int r = ((getRedChannel(targetA) * eva) + (getRedChannel(targetB) * evb)) >> 4;
unsigned int g = ((getGreenChannel(targetA) * eva) + (getGreenChannel(targetB) * evb)) >> 4;
unsigned int b = ((getBlueChannel(targetA) * eva) + (getBlueChannel(targetB) * evb)) >> 4;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
return r | (g << 5) | (b << 10) | (1 << 15);
}
static uint16_t alphaBrightnessIncrease(uint16_t targetA)
{
unsigned int evy = (REG_BLDY & 0x1F);
unsigned int r = getRedChannel(targetA) + (31 - getRedChannel(targetA)) * evy / 16;
unsigned int g = getGreenChannel(targetA) + (31 - getGreenChannel(targetA)) * evy / 16;
unsigned int b = getBlueChannel(targetA) + (31 - getBlueChannel(targetA)) * evy / 16;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
return r | (g << 5) | (b << 10) | (1 << 15);
}
static uint16_t alphaBrightnessDecrease(uint16_t targetA)
{
unsigned int evy = (REG_BLDY & 0x1F);
unsigned int r = getRedChannel(targetA) - getRedChannel(targetA) * evy / 16;
unsigned int g = getGreenChannel(targetA) - getGreenChannel(targetA) * evy / 16;
unsigned int b = getBlueChannel(targetA) - getBlueChannel(targetA) * evy / 16;
if (r > 31)
r = 31;
if (g > 31)
g = 31;
if (b > 31)
b = 31;
return r | (g << 5) | (b << 10) | (1 << 15);
}
//outputs the blended pixel in colorOutput, the prxxx are the bg priority and subpriority, pixelpos is pixel offset in scanline
static bool alphaBlendSelectTargetB(struct scanlineData* scanline, uint16_t* colorOutput, char prnum, char prsub, int pixelpos, bool spriteBlendEnabled)
{
//iterate trough every possible bg to blend with, starting from specified priorities from arguments
for (unsigned int blndprnum = prnum; blndprnum <= 3; blndprnum++)
{
//check if sprite is available to blend with, if sprite blending is enabled
if (spriteBlendEnabled == true && getAlphaBit(scanline->spriteLayers[blndprnum][pixelpos]) == 1)
{
*colorOutput = scanline->spriteLayers[blndprnum][pixelpos];
return true;
}
for (unsigned int blndprsub = prsub; blndprsub < scanline->prioritySortedBgsCount[blndprnum]; blndprsub++)
{
char currLayer = scanline->prioritySortedBgs[blndprnum][blndprsub];
if (getAlphaBit( scanline->layers[currLayer][pixelpos] ) == 1 && REG_BLDCNT & ( 1 << (8 + currLayer)) && isbgEnabled(currLayer))
{
*colorOutput = scanline->layers[currLayer][pixelpos];
return true;
}
//if we hit a non target layer we should bail
if ( getAlphaBit( scanline->layers[currLayer][pixelpos] ) == 1 && isbgEnabled(currLayer) && prnum != blndprnum )
{
return false;
}
}
prsub = 0; //start from zero in the next iteration
}
//no background got hit, check if backdrop is enabled and return it if enabled otherwise fail
if (REG_BLDCNT & BLDCNT_TGT2_BD)
{
*colorOutput = *(uint16_t*)PLTT;
return true;
}
else
{
return false;
}
}
//checks if window horizontal is in bounds and takes account WIN wraparound
static bool winCheckHorizontalBounds(u16 left, u16 right, u16 xpos)
{
if (left > right)
return (xpos >= left || xpos < right);
else
return (xpos >= left && xpos < right);
}
// Parts of this code heavily borrowed from NanoboyAdvance.
static void DrawSprites(struct scanlineData* scanline, uint16_t vcount, bool windowsEnabled)
{
int i;
unsigned int x;
unsigned int y;
void *objtiles = VRAM_ + 0x10000;
unsigned int blendMode = (REG_BLDCNT >> 6) & 3;
bool winShouldBlendPixel = true;
int16_t matrix[2][2] = {};
if (!(REG_DISPCNT & (1 << 6)))
{
puts("2-D OBJ Character mapping not supported.");
}
for (i = 127; i >= 0; i--)
{
struct OamData *oam = &((struct OamData *)OAM)[i];
unsigned int width;
unsigned int height;
uint16_t *pixels;
bool isAffine = oam->affineMode & 1;
bool doubleSizeOrDisabled = (oam->affineMode >> 1) & 1;
bool isSemiTransparent = (oam->objMode == 1);
bool isObjWin = (oam->objMode == 2);
if (!(isAffine) && doubleSizeOrDisabled) // disable for non-affine
{
continue;
}
if (oam->shape == 0)
{
width = (1 << oam->size) * 8;
height = (1 << oam->size) * 8;
}
else if (oam->shape == 1) // wide
{
width = spriteSizes[oam->size][1];
height = spriteSizes[oam->size][0];
}
else if (oam->shape == 2) // tall
{
width = spriteSizes[oam->size][0];
height = spriteSizes[oam->size][1];
}
else
{
continue; // prohibited, do not draw
}
int rect_width = width;
int rect_height = height;
int half_width = width / 2;
int half_height = height / 2;
pixels = scanline->spriteLayers[oam->priority];
int32_t x = oam->x;
int32_t y = oam->y;
if (x >= DISPLAY_WIDTH)
x -= 512;
if (y >= DISPLAY_HEIGHT)
y -= 256;
if (isAffine)
{
//TODO: there is probably a better way to do this
u8 matrixNum = oam->matrixNum * 4;
struct OamData *oam1 = &((struct OamData *)OAM)[matrixNum];
struct OamData *oam2 = &((struct OamData *)OAM)[matrixNum + 1];
struct OamData *oam3 = &((struct OamData *)OAM)[matrixNum + 2];
struct OamData *oam4 = &((struct OamData *)OAM)[matrixNum + 3];
matrix[0][0] = oam1->affineParam;
matrix[0][1] = oam2->affineParam;
matrix[1][0] = oam3->affineParam;
matrix[1][1] = oam4->affineParam;
if (doubleSizeOrDisabled) // double size for affine
{
rect_width *= 2;
rect_height *= 2;
half_width *= 2;
half_height *= 2;
}
}
else
{
// Identity
matrix[0][0] = 0x100;
matrix[0][1] = 0;
matrix[1][0] = 0;
matrix[1][1] = 0x100;
}
x += half_width;
y += half_height;
// Does this sprite actually draw on this scanline?
if (vcount >= (y - half_height) && vcount < (y + half_height))
{
int local_y = (oam->mosaic == 1) ? applySpriteVerticalMosaicEffect(vcount) - y : vcount - y;
int number = oam->tileNum;
int palette = oam->paletteNum;
bool flipX = !isAffine && ((oam->matrixNum >> 3) & 1);
bool flipY = !isAffine && ((oam->matrixNum >> 4) & 1);
bool is8BPP = oam->bpp & 1;
for (int local_x = -half_width; local_x <= half_width; local_x++)
{
uint8_t *tiledata = (uint8_t *)objtiles;
uint16_t *palette = (uint16_t *)(PLTT + 0x200);
int local_mosaicX;
int tex_x;
int tex_y;
unsigned int global_x = local_x + x;
if (global_x < 0 || global_x >= DISPLAY_WIDTH)
continue;
if (oam->mosaic == 1)
{
//mosaic effect has to be applied to global coordinates otherwise the mosaic will scroll
local_mosaicX = applySpriteHorizontalMosaicEffect(global_x) - x;
tex_x = ((matrix[0][0] * local_mosaicX + matrix[0][1] * local_y) >> 8) + (width / 2);
tex_y = ((matrix[1][0] * local_mosaicX + matrix[1][1] * local_y) >> 8) + (height / 2);
}else{
tex_x = ((matrix[0][0] * local_x + matrix[0][1] * local_y) >> 8) + (width / 2);
tex_y = ((matrix[1][0] * local_x + matrix[1][1] * local_y) >> 8) + (height / 2);
}
/* Check if transformed coordinates are inside bounds. */
if (tex_x >= width || tex_y >= height || tex_x < 0 || tex_y < 0)
continue;
if (flipX)
tex_x = width - tex_x - 1;
if (flipY)
tex_y = height - tex_y - 1;
int tile_x = tex_x % 8;
int tile_y = tex_y % 8;
int block_x = tex_x / 8;
int block_y = tex_y / 8;
int block_offset = ((block_y * (REG_DISPCNT & 0x40 ? (width / 8) : 16)) + block_x);
uint16_t pixel = 0;
if (!is8BPP)
{
pixel = tiledata[(block_offset + oam->tileNum) * 32 + (tile_y * 4) + (tile_x / 2)];
if (tile_x & 1)
pixel >>= 4;
else
pixel &= 0xF;
palette += oam->paletteNum * 16;
}
else
{
pixel = tiledata[(block_offset * 2 + oam->tileNum) * 32 + (tile_y * 8) + tile_x];
}
if (pixel != 0)
{
uint16_t color = palette[pixel];;
//if sprite mode is 2 then write to the window mask instead
if (isObjWin)
{
if (scanline->winMask[global_x] & WINMASK_WINOUT)
scanline->winMask[global_x] = (REG_WINOUT >> 8) & 0x3F;
continue;
}
//this code runs if pixel is to be drawn
if (global_x < DISPLAY_WIDTH && global_x >= 0)
{
//check if its enabled in the window (if window is enabled)
winShouldBlendPixel = (windowsEnabled == false || scanline->winMask[global_x] & WINMASK_CLR);
//has to be separated from the blend mode switch statement because of OBJ semi transparancy feature
if ((blendMode == 1 && REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel) || isSemiTransparent)
{
uint16_t targetA = color;
uint16_t targetB = 0;
if (alphaBlendSelectTargetB(scanline, &targetB, oam->priority, 0, global_x, false))
{
color = alphaBlendColor(targetA, targetB);
}
}
else if (REG_BLDCNT & BLDCNT_TGT1_OBJ && winShouldBlendPixel)
{
switch (blendMode)
{
case 2:
color = alphaBrightnessIncrease(color);
break;
case 3:
color = alphaBrightnessDecrease(color);
break;
}
}
//write pixel to pixel framebuffer
pixels[global_x] = color | (1 << 15);
}
}
}
}
}
}
static void DrawScanline(uint16_t *pixels, uint16_t vcount)
{
unsigned int mode = REG_DISPCNT & 3;
unsigned char numOfBgs = (mode == 0 ? 4 : 3);
int bgnum, prnum;
struct scanlineData scanline;
unsigned int blendMode = (REG_BLDCNT >> 6) & 3;
unsigned int xpos;
//initialize all priority bookkeeping data
memset(scanline.layers, 0, sizeof(scanline.layers));
memset(scanline.winMask, 0, sizeof(scanline.winMask));
memset(scanline.spriteLayers, 0, sizeof(scanline.spriteLayers));
memset(scanline.prioritySortedBgsCount, 0, sizeof(scanline.prioritySortedBgsCount));
for (bgnum = 0; bgnum < numOfBgs; bgnum++)
{
uint16_t bgcnt = *(uint16_t*)(REG_ADDR_BG0CNT + bgnum * 2);
uint16_t priority;
scanline.bgcnts[bgnum] = bgcnt;
scanline.bgtoprio[bgnum] = priority = (bgcnt & 3);
char priorityCount = scanline.prioritySortedBgsCount[priority];
scanline.prioritySortedBgs[priority][priorityCount] = bgnum;
scanline.prioritySortedBgsCount[priority]++;
}
switch (mode)
{
case 0:
// All backgrounds are text mode
for (bgnum = 3; bgnum >= 0; bgnum--)
{
if (isbgEnabled(bgnum))
{
uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4);
uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4);
RenderBGScanline(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, scanline.layers[bgnum]);
}
}
break;
case 1:
// BG2 is affine
bgnum = 2;
if (isbgEnabled(bgnum))
{
RenderRotScaleBGScanline(bgnum, scanline.bgcnts[bgnum], REG_BG2X, REG_BG2Y, vcount, scanline.layers[bgnum]);
}
// BG0 and BG1 are text mode
for (bgnum = 1; bgnum >= 0; bgnum--)
{
if (isbgEnabled(bgnum))
{
uint16_t bghoffs = *(uint16_t *)(REG_ADDR_BG0HOFS + bgnum * 4);
uint16_t bgvoffs = *(uint16_t *)(REG_ADDR_BG0VOFS + bgnum * 4);
RenderBGScanline(bgnum, scanline.bgcnts[bgnum], bghoffs, bgvoffs, vcount, scanline.layers[bgnum]);
}
}
break;
default:
printf("Video mode %u is unsupported.\n", mode);
break;
}
bool windowsEnabled = false;
uint16_t WIN0bottom, WIN0top, WIN0right, WIN0left;
uint16_t WIN1bottom, WIN1top, WIN1right, WIN1left;
bool WIN0enable, WIN1enable;
WIN0enable = false;
WIN1enable = false;
//figure out if WIN0 masks on this scanline
if (REG_DISPCNT & DISPCNT_WIN0_ON)
{
//acquire the window coordinates
WIN0bottom = (REG_WIN0V & 0xFF); //y2;
WIN0top = (REG_WIN0V & 0xFF00) >> 8; //y1;
WIN0right = (REG_WIN0H & 0xFF); //x2
WIN0left = (REG_WIN0H & 0xFF00) >> 8; //x1
//figure out WIN Y wraparound and check bounds accordingly
if (WIN0top > WIN0bottom) {
if (vcount >= WIN0top || vcount < WIN0bottom)
WIN0enable = true;
} else {
if (vcount >= WIN0top && vcount < WIN0bottom)
WIN0enable = true;
}
windowsEnabled = true;
}
//figure out if WIN1 masks on this scanline
if (REG_DISPCNT & DISPCNT_WIN1_ON)
{
WIN1bottom = (REG_WIN1V & 0xFF);
WIN1top = (REG_WIN1V & 0xFF00) >> 8;
WIN1right = (REG_WIN1H & 0xFF);
WIN1left = (REG_WIN1H & 0xFF00) >> 8;
if (WIN1top > WIN1bottom) {
if (vcount >= WIN1top || vcount < WIN1bottom)
WIN1enable = true;
} else {
if (vcount >= WIN1top && vcount < WIN1bottom)
WIN1enable = true;
}
windowsEnabled = true;
}
//enable windows if OBJwin is enabled
if (REG_DISPCNT & DISPCNT_OBJWIN_ON && REG_DISPCNT & DISPCNT_OBJ_ON)
{
windowsEnabled = true;
}
//draw to pixel mask
if (windowsEnabled)
{
for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++)
{
//win0 checks
if (WIN0enable && winCheckHorizontalBounds(WIN0left, WIN0right, xpos))
scanline.winMask[xpos] = REG_WININ & 0x3F;
//win1 checks
else if (WIN1enable && winCheckHorizontalBounds(WIN1left, WIN1right, xpos))
scanline.winMask[xpos] = (REG_WININ >> 8) & 0x3F;
else
scanline.winMask[xpos] = (REG_WINOUT & 0x3F) | WINMASK_WINOUT;
}
}
if (REG_DISPCNT & DISPCNT_OBJ_ON)
DrawSprites(&scanline, vcount, windowsEnabled);
//iterate trough every priority in order
for (prnum = 3; prnum >= 0; prnum--)
{
for (char prsub = scanline.prioritySortedBgsCount[prnum] - 1; prsub >= 0; prsub--)
{
char bgnum = scanline.prioritySortedBgs[prnum][prsub];
//if background is enabled then draw it
if (isbgEnabled(bgnum))
{
uint16_t *src = scanline.layers[bgnum];
//copy all pixels to framebuffer
for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++)
{
uint16_t color = src[xpos];
bool winEffectEnable = true;
if (!getAlphaBit(color))
continue; //do nothing if alpha bit is not set
if (windowsEnabled)
{
winEffectEnable = ((scanline.winMask[xpos] & WINMASK_CLR) >> 5);
//if bg is disabled inside the window then do not draw the pixel
if ( !(scanline.winMask[xpos] & 1 << bgnum) )
continue;
}
//blending code
if (blendMode != 0 && REG_BLDCNT & (1 << bgnum) && winEffectEnable)
{
uint16_t targetA = color;
uint16_t targetB = 0;
char isSpriteBlendingEnabled;
switch (blendMode)
{
case 1:
isSpriteBlendingEnabled = REG_BLDCNT & BLDCNT_TGT2_OBJ ? 1 : 0;
//find targetB and blend it
if (alphaBlendSelectTargetB(&scanline, &targetB, prnum, prsub+1, xpos, isSpriteBlendingEnabled))
{
color = alphaBlendColor(targetA, targetB);
}
break;
case 2:
color = alphaBrightnessIncrease(targetA);
break;
case 3:
color = alphaBrightnessDecrease(targetA);
break;
}
}
//write the pixel to scanline buffer output
pixels[xpos] = color;
}
}
}
//draw sprites on current priority
uint16_t *src = scanline.spriteLayers[prnum];
for (xpos = 0; xpos < DISPLAY_WIDTH; xpos++)
{
if (getAlphaBit(src[xpos]))
{
//check if sprite pixel draws inside window
if (windowsEnabled && !(scanline.winMask[xpos] & WINMASK_OBJ))
continue;
//draw the pixel
pixels[xpos] = src[xpos];
}
}
}
}
uint16_t *memsetu16(uint16_t *dst, uint16_t fill, size_t count)
{
for (int i = 0; i < count; i++)
{
*dst++ = fill;
}
}
void DrawFrame(uint16_t *pixels)
{
int i;
int j;
for (i = 0; i < DISPLAY_HEIGHT; i++)
{
REG_VCOUNT = i;
if(((REG_DISPSTAT >> 8) & 0xFF) == REG_VCOUNT)
{
REG_DISPSTAT |= INTR_FLAG_VCOUNT;
if(REG_DISPSTAT & DISPSTAT_VCOUNT_INTR)
gIntrTable[0]();
}
// Render the backdrop color before the each individual scanline.
// backdrop color brightness effects
unsigned int blendMode = (REG_BLDCNT >> 6) & 3;
uint16_t backdropColor = *(uint16_t *)PLTT;
if (REG_BLDCNT & BLDCNT_TGT1_BD)
{
switch (blendMode)
{
case 2:
backdropColor = alphaBrightnessIncrease(backdropColor);
break;
case 3:
backdropColor = alphaBrightnessDecrease(backdropColor);
break;
}
}
memsetu16(&pixels[i * DISPLAY_WIDTH], backdropColor, DISPLAY_WIDTH);
DrawScanline(&pixels[i * DISPLAY_WIDTH], i);
REG_DISPSTAT |= INTR_FLAG_HBLANK;
RunDMAs(DMA_HBLANK);
if (REG_DISPSTAT & DISPSTAT_HBLANK_INTR)
gIntrTable[3]();
REG_DISPSTAT &= ~INTR_FLAG_HBLANK;
REG_DISPSTAT &= ~INTR_FLAG_VCOUNT;
}
}

2728
src/platform/gba_fast_draw.c Normal file

File diff suppressed because it is too large Load Diff

513
src/platform/sdl2.c Normal file
View File

@ -0,0 +1,513 @@
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#include <xinput.h>
#endif
#include <SDL2/SDL.h>
#include "global.h"
#include "platform.h"
#include "gba/defines.h"
#include "gba/m4a_internal.h"
#include "cgb_audio.h"
#include "gba/flash_internal.h"
#include "platform/dma.h"
#include "platform/framedraw.h"
extern IntrFunc gIntrTable[];
SDL_Thread *mainLoopThread;
SDL_Window *sdlWindow;
SDL_Renderer *sdlRenderer;
SDL_Texture *sdlTexture;
SDL_sem *vBlankSemaphore;
SDL_atomic_t isFrameAvailable;
bool speedUp = false;
unsigned int videoScale = 1;
bool videoScaleChanged = false;
bool isRunning = true;
bool paused = false;
double simTime = 0;
double lastGameTime = 0;
double curGameTime = 0;
double fixedTimestep = 1.0 / 60.0; // 16.666667ms
double timeScale = 1.0;
static FILE *sSaveFile = NULL;
extern void AgbMain(void);
extern void DoSoftReset(void);
int DoMain(void *param);
void ProcessEvents(void);
void VDraw(SDL_Texture *texture);
static void ReadSaveFile(char *path);
static void StoreSaveFile(void);
static void CloseSaveFile(void);
int main(int argc, char **argv)
{
// Open an output console on Windows
#ifdef _WIN32
AllocConsole() ;
AttachConsole( GetCurrentProcessId() ) ;
freopen( "CON", "w", stdout ) ;
#endif
ReadSaveFile("pokefirered.sav");
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
{
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
return 1;
}
sdlWindow = SDL_CreateWindow("pokefirered", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DISPLAY_WIDTH * videoScale, DISPLAY_HEIGHT * videoScale, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (sdlWindow == NULL)
{
printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
return 1;
}
sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC);
if (sdlRenderer == NULL)
{
printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError());
return 1;
}
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 255, 255);
SDL_RenderClear(sdlRenderer);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
SDL_RenderSetLogicalSize(sdlRenderer, DISPLAY_WIDTH, DISPLAY_HEIGHT);
sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ABGR1555,
SDL_TEXTUREACCESS_STREAMING,
DISPLAY_WIDTH, DISPLAY_HEIGHT);
if (sdlTexture == NULL)
{
printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError());
return 1;
}
simTime = curGameTime = lastGameTime = SDL_GetPerformanceCounter();
isFrameAvailable.value = 0;
vBlankSemaphore = SDL_CreateSemaphore(0);
SDL_AudioSpec want;
SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */
want.freq = 42048;
want.format = AUDIO_F32;
want.channels = 2;
want.samples = 1024;
cgb_audio_init(want.freq);
if (SDL_OpenAudio(&want, 0) < 0)
SDL_Log("Failed to open audio: %s", SDL_GetError());
else
{
if (want.format != AUDIO_F32) /* we let this one thing change. */
SDL_Log("We didn't get Float32 audio format.");
SDL_PauseAudio(0);
}
VDraw(sdlTexture);
mainLoopThread = SDL_CreateThread(DoMain, "AgbMain", NULL);
double accumulator = 0.0;
while (isRunning)
{
ProcessEvents();
if (!paused)
{
double dt = fixedTimestep / timeScale; // TODO: Fix speedup
curGameTime = SDL_GetPerformanceCounter();
double deltaTime = (double)((curGameTime - lastGameTime) / (double)SDL_GetPerformanceFrequency());
if (deltaTime > (dt * 5))
deltaTime = dt;
lastGameTime = curGameTime;
accumulator += deltaTime;
while (accumulator >= dt)
{
if (SDL_AtomicGet(&isFrameAvailable))
{
VDraw(sdlTexture);
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_AtomicSet(&isFrameAvailable, 0);
REG_DISPSTAT |= INTR_FLAG_VBLANK;
RunDMAs(DMA_HBLANK);
if (REG_DISPSTAT & DISPSTAT_VBLANK_INTR){
gIntrTable[4]();
}
REG_DISPSTAT &= ~INTR_FLAG_VBLANK;
SDL_SemPost(vBlankSemaphore);
accumulator -= dt;
}
}
}
if (videoScaleChanged)
{
SDL_SetWindowSize(sdlWindow, DISPLAY_WIDTH * videoScale, DISPLAY_HEIGHT * videoScale);
videoScaleChanged = false;
}
SDL_RenderPresent(sdlRenderer);
}
//StoreSaveFile();
CloseSaveFile();
SDL_DestroyWindow(sdlWindow);
SDL_Quit();
return 0;
}
static void ReadSaveFile(char *path)
{
// Check whether the saveFile exists, and create it if not
sSaveFile = fopen(path, "r+b");
if (sSaveFile == NULL)
{
sSaveFile = fopen(path, "w+b");
}
fseek(sSaveFile, 0, SEEK_END);
int fileSize = ftell(sSaveFile);
fseek(sSaveFile, 0, SEEK_SET);
// Only read as many bytes as fit inside the buffer
// or as many bytes as are in the file
int bytesToRead = (fileSize < sizeof(FLASH_BASE)) ? fileSize : sizeof(FLASH_BASE);
int bytesRead = fread(FLASH_BASE, 1, bytesToRead, sSaveFile);
// Fill the buffer if the savefile was just created or smaller than the buffer itself
for (int i = bytesRead; i < sizeof(FLASH_BASE); i++)
{
FLASH_BASE[i] = 0xFF;
}
}
static void StoreSaveFile()
{
if (sSaveFile != NULL)
{
fseek(sSaveFile, 0, SEEK_SET);
fwrite(FLASH_BASE, 1, sizeof(FLASH_BASE), sSaveFile);
}
}
void Platform_StoreSaveFile(void)
{
StoreSaveFile();
}
void Platform_ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size)
{
printf("ReadFlash(sectorNum=0x%04X,offset=0x%08X,size=0x%02X)\n",sectorNum,offset,size);
FILE * savefile = fopen("pokefirered.sav", "r+b");
if (savefile == NULL)
{
puts("Error opening save file.");
return;
}
if (fseek(savefile, (sectorNum << gFlash->sector.shift) + offset, SEEK_SET))
{
fclose(savefile);
return;
}
if (fread(dest, 1, size, savefile) != size)
{
fclose(savefile);
return;
}
fclose(savefile);
}
void Platform_QueueAudio(float *audioBuffer, s32 samplesPerFrame)
{
SDL_QueueAudio(1, audioBuffer, samplesPerFrame);
}
static void CloseSaveFile()
{
if (sSaveFile != NULL)
{
fclose(sSaveFile);
}
}
// Key mappings
#define KEY_A_BUTTON SDLK_z
#define KEY_B_BUTTON SDLK_x
#define KEY_START_BUTTON SDLK_RETURN
#define KEY_SELECT_BUTTON SDLK_BACKSLASH
#define KEY_L_BUTTON SDLK_a
#define KEY_R_BUTTON SDLK_s
#define KEY_DPAD_UP SDLK_UP
#define KEY_DPAD_DOWN SDLK_DOWN
#define KEY_DPAD_LEFT SDLK_LEFT
#define KEY_DPAD_RIGHT SDLK_RIGHT
#define HANDLE_KEYUP(key) \
case KEY_##key: keys &= ~key; break;
#define HANDLE_KEYDOWN(key) \
case KEY_##key: keys |= key; break;
static u16 keys;
void ProcessEvents(void)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
isRunning = false;
break;
case SDL_KEYUP:
switch (event.key.keysym.sym)
{
HANDLE_KEYUP(A_BUTTON)
HANDLE_KEYUP(B_BUTTON)
HANDLE_KEYUP(START_BUTTON)
HANDLE_KEYUP(SELECT_BUTTON)
HANDLE_KEYUP(L_BUTTON)
HANDLE_KEYUP(R_BUTTON)
HANDLE_KEYUP(DPAD_UP)
HANDLE_KEYUP(DPAD_DOWN)
HANDLE_KEYUP(DPAD_LEFT)
HANDLE_KEYUP(DPAD_RIGHT)
case SDLK_SPACE:
if (speedUp)
{
speedUp = false;
timeScale = 1.0;
SDL_ClearQueuedAudio(1);
SDL_PauseAudio(0);
}
break;
}
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
HANDLE_KEYDOWN(A_BUTTON)
HANDLE_KEYDOWN(B_BUTTON)
HANDLE_KEYDOWN(START_BUTTON)
HANDLE_KEYDOWN(SELECT_BUTTON)
HANDLE_KEYDOWN(L_BUTTON)
HANDLE_KEYDOWN(R_BUTTON)
HANDLE_KEYDOWN(DPAD_UP)
HANDLE_KEYDOWN(DPAD_DOWN)
HANDLE_KEYDOWN(DPAD_LEFT)
HANDLE_KEYDOWN(DPAD_RIGHT)
case SDLK_r:
if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL))
{
DoSoftReset();
}
break;
case SDLK_p:
if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL))
{
paused = !paused;
}
break;
case SDLK_SPACE:
if (!speedUp)
{
speedUp = true;
timeScale = 5.0;
SDL_PauseAudio(1);
}
break;
}
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
{
unsigned int w = event.window.data1;
unsigned int h = event.window.data2;
videoScale = 0;
if (w / DISPLAY_WIDTH > videoScale)
videoScale = w / DISPLAY_WIDTH;
if (h / DISPLAY_HEIGHT > videoScale)
videoScale = h / DISPLAY_HEIGHT;
if (videoScale < 1)
videoScale = 1;
videoScaleChanged = true;
}
break;
}
}
}
#ifdef _WIN32
#define STICK_THRESHOLD 0.5f
u16 GetXInputKeys()
{
XINPUT_STATE state;
ZeroMemory(&state, sizeof(XINPUT_STATE));
DWORD dwResult = XInputGetState(0, &state);
u16 xinputKeys = 0;
if (dwResult == ERROR_SUCCESS)
{
/* A */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) >> 12;
/* B */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) >> 13;
/* Start */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) >> 1;
/* Select */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) >> 3;
/* L */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) << 1;
/* R */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) >> 1;
/* Up */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) << 6;
/* Down */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) << 6;
/* Left */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) << 3;
/* Right */ xinputKeys |= (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) << 1;
/* Control Stick */
float xAxis = (float)state.Gamepad.sThumbLX / (float)SHRT_MAX;
float yAxis = (float)state.Gamepad.sThumbLY / (float)SHRT_MAX;
if (xAxis < -STICK_THRESHOLD) xinputKeys |= DPAD_LEFT;
if (xAxis > STICK_THRESHOLD) xinputKeys |= DPAD_RIGHT;
if (yAxis < -STICK_THRESHOLD) xinputKeys |= DPAD_DOWN;
if (yAxis > STICK_THRESHOLD) xinputKeys |= DPAD_UP;
/* Speedup */
// Note: 'speedup' variable is only (un)set on keyboard input
double oldTimeScale = timeScale;
timeScale = (state.Gamepad.bRightTrigger > 0x80 || speedUp) ? 5.0 : 1.0;
if (oldTimeScale != timeScale)
{
if (timeScale > 1.0)
{
SDL_PauseAudio(1);
}
else
{
SDL_ClearQueuedAudio(1);
SDL_PauseAudio(0);
}
}
}
return xinputKeys;
}
#endif // _WIN32
u16 Platform_GetKeyInput(void)
{
#ifdef _WIN32
u16 gamepadKeys = GetXInputKeys();
return (gamepadKeys != 0) ? gamepadKeys : keys;
#endif
return keys;
}
void VDraw(SDL_Texture *texture)
{
static uint16_t image[DISPLAY_WIDTH * DISPLAY_HEIGHT];
memset(image, 0, sizeof(image));
DrawFrame(image);
SDL_UpdateTexture(texture, NULL, image, DISPLAY_WIDTH * sizeof (Uint16));
REG_VCOUNT = 161; // prep for being in VBlank period
}
int DoMain(void *data)
{
AgbMain();
}
void VBlankIntrWait(void)
{
SDL_AtomicSet(&isFrameAvailable, 1);
SDL_SemWait(vBlankSemaphore);
}
void printRegs(){
printf("REG_DISPCNT %04X\n", REG_DISPCNT);
printf("REG_DISPSTAT %04X\n", REG_DISPSTAT);
printf("REG_VCOUNT %04X\n", REG_VCOUNT);
printf("REG_BG0CNT %04X\n", REG_BG0CNT);
printf("REG_BG1CNT %04X\n", REG_BG1CNT);
printf("REG_BG2CNT %04X\n", REG_BG2CNT);
printf("REG_BG3CNT %04X\n", REG_BG3CNT);
printf("REG_BG0HOFS %04X\n", REG_BG0HOFS);
printf("REG_BG0VOFS %04X\n", REG_BG0VOFS);
printf("REG_BG1HOFS %04X\n", REG_BG1HOFS);
printf("REG_BG1VOFS %04X\n", REG_BG1VOFS);
printf("REG_BG2HOFS %04X\n", REG_BG2HOFS);
printf("REG_BG2VOFS %04X\n", REG_BG2VOFS);
printf("REG_BG3HOFS %04X\n", REG_BG3HOFS);
printf("REG_BG3VOFS %04X\n", REG_BG3VOFS);
printf("REG_BG2PA %04X\n", REG_BG2PA);
printf("REG_BG2PB %04X\n", REG_BG2PB);
printf("REG_BG2PC %04X\n", REG_BG2PC);
printf("REG_BG2PD %04X\n", REG_BG2PD);
printf("REG_BG2X %04X\n", REG_BG2X);
printf("REG_BG2X_L %04X\n", REG_BG2X_L);
printf("REG_BG2X_H %04X\n", REG_BG2X_H);
printf("REG_BG2Y %04X\n", REG_BG2Y);
printf("REG_BG2Y_L %04X\n", REG_BG2Y_L);
printf("REG_BG2Y_H %04X\n", REG_BG2Y_H);
printf("REG_BG3PA %04X\n", REG_BG3PA);
printf("REG_BG3PB %04X\n", REG_BG3PB);
printf("REG_BG3PC %04X\n", REG_BG3PC);
printf("REG_BG3PD %04X\n", REG_BG3PD);
printf("REG_BG3X %04X\n", REG_BG3X);
printf("REG_BG3X_L %04X\n", REG_BG3X_L);
printf("REG_BG3X_H %04X\n", REG_BG3X_H);
printf("REG_BG3Y %04X\n", REG_BG3Y);
printf("REG_BG3Y_L %04X\n", REG_BG3Y_L);
printf("REG_BG3Y_H %04X\n", REG_BG3Y_H);
printf("REG_WIN0H %04X\n", REG_WIN0H);
printf("REG_WIN1H %04X\n", REG_WIN1H);
printf("REG_WIN0V %04X\n", REG_WIN0V);
printf("REG_WIN1V %04X\n", REG_WIN1V);
printf("REG_WININ %04X\n", REG_WININ);
printf("REG_WINOUT %04X\n", REG_WINOUT);
printf("REG_MOSAIC %04X\n", REG_MOSAIC);
printf("REG_BLDCNT %04X\n", REG_BLDCNT);
printf("REG_BLDALPHA %04X\n", REG_BLDALPHA);
printf("REG_BLDY %04X\n", REG_BLDY);
printf("____________________________________\n");
}

View File

@ -682,7 +682,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId)
u8 wantedCry = gTasks[taskId].tCryTaskWantedCry;
s8 pan = gTasks[taskId].tCryTaskPan;
u16 species = gTasks[taskId].tCryTaskSpecies;
struct Pokemon *mon = (void *)(u32)((gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2));
struct Pokemon *mon = (void *)(u32)(((u16)gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2));
switch (gTasks[taskId].tCryTaskState)
{

View File

@ -691,6 +691,7 @@ u8 HandleSavingData(u8 saveType)
WriteSaveSectorOrSlot(FULL_SAVE_SLOT, gRamSaveSectorLocations);
break;
}
Platform_StoreSaveFile();
gMain.vblankCounter1 = backupPtr;
#if REVISION >= 0xA
svc_FinishSave();
@ -709,6 +710,7 @@ u8 TrySavingData(u8 saveType)
HandleSavingData(saveType);
if (!gDamagedSaveSectors)
{
Platform_StoreSaveFile();
gSaveAttemptStatus = SAVE_STATUS_OK;
return SAVE_STATUS_OK;
}

487
src/sound_mixer.c Normal file
View File

@ -0,0 +1,487 @@
#include "global.h"
#include "music_player.h"
#include "sound_mixer.h"
#include "mp2k_common.h"
#include "cgb_audio.h"
#define VCOUNT_VBLANK 160
#define TOTAL_SCANLINES 228
static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData2 *wav, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal);
void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *outBuffer, u8 dmaCounter, u16 maxBufSize);
static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData2 *wav);
void GeneratePokemonSampleAudio(struct SoundMixerState *mixer, struct MixerSource *chan, s8 *current, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal, s32 samplesLeftInWav, signed envR, signed envL, s32 loopLen);
static s8 sub_82DF758(struct MixerSource *chan, u32 current);
void RunMixerFrame(void) {
struct SoundMixerState *mixer = SOUND_INFO_PTR;
if (mixer->lockStatus != MIXER_UNLOCKED) {
return;
}
mixer->lockStatus = MIXER_LOCKED;
u32 maxScanlines = mixer->maxScanlines;
if (mixer->maxScanlines != 0) {
u32 vcount = REG_VCOUNT;
maxScanlines += vcount;
if (vcount < VCOUNT_VBLANK) {
maxScanlines += TOTAL_SCANLINES;
}
}
if (mixer->firstPlayerFunc != NULL) {
mixer->firstPlayerFunc(mixer->firstPlayer);
}
mixer->cgbMixerFunc();
s32 samplesPerFrame = mixer->samplesPerFrame;
float *outBuffer = mixer->outBuffer;
s32 dmaCounter = mixer->dmaCounter;
if (dmaCounter > 1) {
outBuffer += samplesPerFrame * (mixer->framesPerDmaCycle - (dmaCounter - 1)) * 2;
}
//MixerRamFunc mixerRamFunc = ((MixerRamFunc)MixerCodeBuffer);
SampleMixer(mixer, maxScanlines, samplesPerFrame, outBuffer, dmaCounter, MIXED_AUDIO_BUFFER_SIZE);
cgb_audio_generate(samplesPerFrame);
}
//__attribute__((target("thumb")))
void SampleMixer(struct SoundMixerState *mixer, u32 scanlineLimit, u16 samplesPerFrame, float *outBuffer, u8 dmaCounter, u16 maxBufSize) {
u32 reverb = mixer->reverb;
if (reverb) {
// The vanilla reverb effect outputs a mono sound from four sources:
// - L/R channels as they were mixer->framesPerDmaCycle frames ago
// - L/R channels as they were (mixer->framesPerDmaCycle - 1) frames ago
float *tmp1 = outBuffer;
float *tmp2;
if (dmaCounter == 2) {
tmp2 = mixer->outBuffer;
} else {
tmp2 = outBuffer + samplesPerFrame * 2;
}
uf16 i = 0;
do {
float s = tmp1[0] + tmp1[1] + tmp2[0] + tmp2[1];
s *= ((float)reverb / 512.0f);
tmp1[0] = tmp1[1] = s;
tmp1+=2;
tmp2+=2;
}
while(++i < samplesPerFrame);
} else {
// memset(outBuffer, 0, samplesPerFrame);
// memset(outBuffer + maxBufSize, 0, samplesPerFrame);
for (int i = 0; i < samplesPerFrame; i++) {
float *dst = &outBuffer[i*2];
dst[1] = dst[0] = 0.0f;
}
}
float sampleRateReciprocal = mixer->sampleRateReciprocal;
sf8 numChans = mixer->numChans;
struct MixerSource *chan = mixer->chans;
for (int i = 0; i < numChans; i++, chan++) {
struct WaveData2 *wav = chan->wav;
if (scanlineLimit != 0) {
uf16 vcount = REG_VCOUNT;
if (vcount < VCOUNT_VBLANK) {
vcount += TOTAL_SCANLINES;
}
if (vcount >= scanlineLimit) {
goto returnEarly;
}
}
if (TickEnvelope(chan, wav))
{
GenerateAudio(mixer, chan, wav, outBuffer, samplesPerFrame, sampleRateReciprocal);
}
}
returnEarly:
mixer->lockStatus = MIXER_UNLOCKED;
}
// Returns TRUE if channel is still active after moving envelope forward a frame
//__attribute__((target("thumb")))
static inline bool32 TickEnvelope(struct MixerSource *chan, struct WaveData2 *wav) {
// MP2K envelope shape
// |
// (linear)^ |
// Attack / \Decay (exponential) |
// / \_ |
// / '., Sustain |
// / '.______________ |
// / '-. Echo (linear) |
// / Release (exp) ''--..|\ |
// / \ |
u8 status = chan->status;
if ((status & 0xC7) == 0) {
return FALSE;
}
u8 env = 0;
if ((status & 0x80) == 0) {
env = chan->envelopeVol;
if (status & 4) {
// Note-wise echo
--chan->echoVol;
if (chan->echoVol <= 0) {
chan->status = 0;
return FALSE;
} else {
return TRUE;
}
} else if (status & 0x40) {
// Release
chan->envelopeVol = env * chan->release / 256U;
u8 echoVol = chan->echoVol;
if (chan->envelopeVol > echoVol) {
return TRUE;
} else if (echoVol == 0) {
chan->status = 0;
return FALSE;
} else {
chan->status |= 4;
return TRUE;
}
}
switch (status & 3) {
uf16 newEnv;
case 2:
// Decay
chan->envelopeVol = env * chan->decay / 256U;
u8 sustain = chan->sustain;
if (chan->envelopeVol <= sustain && sustain == 0) {
// Duplicated echo check from Release section above
if (chan->echoVol == 0) {
chan->status = 0;
return FALSE;
} else {
chan->status |= 4;
return TRUE;
}
} else if (chan->envelopeVol <= sustain) {
chan->envelopeVol = sustain;
--chan->status;
}
break;
case 3:
attack:
newEnv = env + chan->attack;
if (newEnv > 0xFF) {
chan->envelopeVol = 0xFF;
--chan->status;
} else {
chan->envelopeVol = newEnv;
}
break;
case 1: // Sustain
default:
break;
}
return TRUE;
} else if (status & 0x40) {
// Init and stop cancel each other out
chan->status = 0;
return FALSE;
} else {
// Init channel
chan->status = 3;
#ifdef POKEMON_EXTENSIONS
chan->current = wav->data + chan->ct;
chan->ct = wav->size - chan->ct;
#else
chan->current = wav->data;
chan->ct = wav->size;
#endif
chan->fw = 0;
chan->envelopeVol = 0;
if (wav->loopFlags & 0xC0) {
chan->status |= 0x10;
}
goto attack;
}
}
//__attribute__((target("thumb")))
static inline void GenerateAudio(struct SoundMixerState *mixer, struct MixerSource *chan, struct WaveData2 *wav, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal) {/*, [[[]]]) {*/
uf8 v = chan->envelopeVol * (mixer->masterVol + 1) / 16U;
chan->envelopeVolR = chan->rightVol * v / 256U;
chan->envelopeVolL = chan->leftVol * v / 256U;
s32 loopLen = 0;
s8 *loopStart;
if (chan->status & 0x10) {
loopStart = wav->data + wav->loopStart;
loopLen = wav->size - wav->loopStart;
}
s32 samplesLeftInWav = chan->ct;
s8 *current = chan->current;
signed envR = chan->envelopeVolR;
signed envL = chan->envelopeVolL;
#ifdef POKEMON_EXTENSIONS
if (chan->type & 0x30) {
GeneratePokemonSampleAudio(mixer, chan, current, outBuffer, samplesPerFrame, sampleRateReciprocal, samplesLeftInWav, envR, envL, loopLen);
}
else
#endif
if (chan->type & 8) {
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
sf8 c = *(current++);
outBuffer[1] += (c * envR) / 32768.0f;
outBuffer[0] += (c * envL) / 32768.0f;
if (--samplesLeftInWav == 0) {
samplesLeftInWav = loopLen;
if (loopLen != 0) {
current = loopStart;
} else {
chan->status = 0;
return;
}
}
}
chan->ct = samplesLeftInWav;
chan->current = current;
} else {
float finePos = chan->fw;
float romSamplesPerOutputSample = chan->freq * sampleRateReciprocal;
sf16 b = current[0];
sf16 m = current[1] - b;
current += 1;
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
// Use linear interpolation to calculate a value between the current sample in the wav
// and the next sample. Also cancel out the 9.23 stuff
float sample = (finePos * m) + b;
outBuffer[1] += (sample * envR) / 32768.0f;
outBuffer[0] += (sample * envL) / 32768.0f;
finePos += romSamplesPerOutputSample;
u32 newCoarsePos = finePos;
if (newCoarsePos != 0) {
finePos -= (int)finePos;
samplesLeftInWav -= newCoarsePos;
if (samplesLeftInWav <= 0) {
if (loopLen != 0) {
current = loopStart;
newCoarsePos = -samplesLeftInWav;
samplesLeftInWav += loopLen;
while (samplesLeftInWav <= 0) {
newCoarsePos -= loopLen;
samplesLeftInWav += loopLen;
}
b = current[newCoarsePos];
m = current[newCoarsePos + 1] - b;
current += newCoarsePos + 1;
} else {
chan->status = 0;
return;
}
} else {
b = current[newCoarsePos - 1];
m = current[newCoarsePos] - b;
current += newCoarsePos;
}
}
}
chan->fw = finePos;
chan->ct = samplesLeftInWav;
chan->current = current - 1;
}
}
struct WaveData
{
u16 type;
u16 status;
u32 freq;
u32 loopStart;
u32 size; // number of samples
s8 data[1]; // samples
};
void GeneratePokemonSampleAudio(struct SoundMixerState *mixer, struct MixerSource *chan, s8 *current, float *outBuffer, u16 samplesPerFrame, float sampleRateReciprocal, s32 samplesLeftInWav, signed envR, signed envL, s32 loopLen) {
struct WaveData *wav = chan->wav; // r6
float finePos = chan->fw;
if((chan->status & 0x20) == 0) {
chan->status |= 0x20;
if(chan->type & 0x10) {
s8 *waveEnd = wav->data + wav->size;
current = wav->data + (waveEnd - current);
chan->current = current;
}
if(wav->type != 0) {
current -= (uintptr_t)&wav->data;
chan->current = current;
}
}
float romSamplesPerOutputSample = chan->type & 8 ? 1.0f : chan->freq * sampleRateReciprocal;
if(wav->type != 0) { // is compressed
chan->blockCount = 0xFF000000;
if(chan->type & 0x10) { // is reverse
current -= 1;
sf16 b = sub_82DF758(chan, (uintptr_t)current);
sf16 m = sub_82DF758(chan, (uintptr_t)current - 1) - b;
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
float sample = (finePos * m) + b;
outBuffer[1] += (sample * envR) / 32768.0f;
outBuffer[0] += (sample * envL) / 32768.0f;
finePos += romSamplesPerOutputSample;
int newCoarsePos = finePos;
if (newCoarsePos != 0) {
finePos -= (int)finePos;
samplesLeftInWav -= newCoarsePos;
if (samplesLeftInWav <= 0) {
chan->status = 0;
break;
}
else {
current -= newCoarsePos;
b = sub_82DF758(chan, (uintptr_t)current);
m = sub_82DF758(chan, (uintptr_t)current - 1) - b;
}
}
}
chan->fw = finePos;
chan->ct = samplesLeftInWav;
chan->current = current + 1;
}
else {
sf16 b = sub_82DF758(chan, (uintptr_t)current);
sf16 m = sub_82DF758(chan, (uintptr_t)current + 1) - b;
current += 1;
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
float sample = (finePos * m) + b;
outBuffer[1] += (sample * envR) / 32768.0f;
outBuffer[0] += (sample * envL) / 32768.0f;
finePos += romSamplesPerOutputSample;
u32 newCoarsePos = finePos; // lr
if (newCoarsePos != 0) {
finePos -= (int)finePos;
samplesLeftInWav -= newCoarsePos;
if (samplesLeftInWav <= 0) {
if (loopLen != 0) {
current = chan->wav->loopStart;
newCoarsePos = -samplesLeftInWav;
samplesLeftInWav += loopLen;
while (samplesLeftInWav <= 0) {
newCoarsePos -= loopLen;
samplesLeftInWav += loopLen;
}
current += newCoarsePos;
b = sub_82DF758(chan, (uintptr_t)current);
m = sub_82DF758(chan, (uintptr_t)current + 1) - b;
current += 1;
} else {
chan->status = 0;
return;
}
} else {
current += newCoarsePos - 1;
b = sub_82DF758(chan, (uintptr_t)current);
m = sub_82DF758(chan, (uintptr_t)current + 1) - b;
current += 1;
}
}
}
chan->fw = finePos;
chan->ct = samplesLeftInWav;
chan->current = current - 1;
}
}
else {
if(chan->type & 0x10) { // is reverse
current -= 1;
sf16 b = current[0];
sf16 m = current[-1] - b;
for (u16 i = 0; i < samplesPerFrame; i++, outBuffer+=2) {
float sample = (finePos * m) + b;
outBuffer[1] += (sample * envR) / 32768.0f;
outBuffer[0] += (sample * envL) / 32768.0f;
finePos += romSamplesPerOutputSample;
int newCoarsePos = finePos;
if (newCoarsePos != 0) {
finePos -= (int)finePos;
samplesLeftInWav -= newCoarsePos;
if (samplesLeftInWav <= 0) {
chan->status = 0;
break;
}
else {
current -= newCoarsePos;
b = current[0];
m = current[-1] - b;
}
}
}
chan->fw = finePos;
chan->ct = samplesLeftInWav;
chan->current = current + 1;
}
}
}
s8 gBDPCMBlockBuffer[64];
extern const s8 gDeltaEncodingTable[];
static s8 sub_82DF758(struct MixerSource *chan, u32 current) {
u32 blockOffset = current >> 6; // current / 64
u8 * blockPtr;
int i;
//In route 102 lotad wild battle when it growls crashes the game because it decompresses out of bounds data
//I gave it its own printf error so it wouldn't get forgotten as this needs a more proper fix
if (chan->wav->size < blockOffset * 0x21) {
printf("Out of bounds decompress in %s wav->size = %u blockPtr = %u\n", __func__, chan->wav->size, blockOffset * 0x21);
return gBDPCMBlockBuffer[current & 63];
}
if(chan->blockCount != blockOffset) { // decode block if not decoded
s32 s;
chan->blockCount = blockOffset;
blockPtr = chan->wav->data + chan->blockCount * 0x21;
gBDPCMBlockBuffer[0] = s = (s8)*blockPtr++;
gBDPCMBlockBuffer[1] = s += gDeltaEncodingTable[*blockPtr++ & 0xF];
for(i = 2; i < 64; i+=2) {
u32 temp = *blockPtr++;
gBDPCMBlockBuffer[i] = s += gDeltaEncodingTable[temp >> 4];
gBDPCMBlockBuffer[i+1] = s += gDeltaEncodingTable[temp & 0xF];
}
}
return gBDPCMBlockBuffer[current & 63]; // index same as current % 64
}

View File

@ -3536,71 +3536,7 @@ void InitUnionRoom(void)
static void Task_InitUnionRoom(u8 taskId)
{
s32 i;
u8 text[32];
struct WirelessLink_URoom * data = sWirelessLinkMain.uRoom;
switch (data->state)
{
case 0:
data->state = 1;
break;
case 1:
SetHostRfuGameData(ACTIVITY_SEARCH, 0, FALSE);
SetWirelessCommType1();
OpenLink();
InitializeRfuLinkManager_EnterUnionRoom();
RfuSetIgnoreError(TRUE);
data->state = 2;
break;
case 2:
data->incomingChildList = AllocZeroed(RFU_CHILD_MAX * sizeof(struct RfuIncomingPlayer));
ClearIncomingPlayerList(data->incomingChildList->players, RFU_CHILD_MAX);
data->incomingParentList = AllocZeroed(RFU_CHILD_MAX * sizeof(struct RfuIncomingPlayer));
ClearIncomingPlayerList(data->incomingParentList->players, RFU_CHILD_MAX);
data->playerList = AllocZeroed(MAX_UNION_ROOM_LEADERS * sizeof(struct RfuPlayer));
ClearRfuPlayerList(data->playerList->players, MAX_UNION_ROOM_LEADERS);
data->spawnPlayer = AllocZeroed(sizeof(struct RfuPlayer));
ClearRfuPlayerList(&data->spawnPlayer->players[0], 1);
data->searchTaskId = CreateTask_SearchForChildOrParent(data->incomingParentList, data->incomingChildList, LINK_GROUP_UNION_ROOM_INIT);
data->state = 3;
break;
case 3:
switch (HandlePlayerListUpdate())
{
case PLIST_NEW_PLAYER:
case PLIST_RECENT_UPDATE:
if (sUnionRoomPlayerName[0] == EOS)
{
for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++)
{
if (data->playerList->players[i].groupScheduledAnim == UNION_ROOM_SPAWN_IN)
{
CopyAndTranslatePlayerName2(text, data->playerList->players[i]);
if (PlayerHasMetTrainerBefore(ReadAsU16(data->playerList->players[i].rfu.data.compatibility.playerTrainerId), text))
{
StringCopy(sUnionRoomPlayerName, text);
break;
}
}
}
}
break;
case PLIST_UNUSED:
break;
}
break;
case 4:
Free(data->spawnPlayer);
Free(data->playerList);
Free(data->incomingParentList);
Free(data->incomingChildList);
DestroyTask(data->searchTaskId);
Free(sWirelessLinkMain.uRoom);
LinkRfu_Shutdown();
DestroyTask(taskId);
break;
}
}
bool16 BufferUnionRoomPlayerName(void)