librfu through 0x81E13F0
This commit is contained in:
-1943
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
gRfuState
|
||||
gRfuSlotStatusUNI
|
||||
gRfuSlotStatusNI
|
||||
gRfuLinkStatus
|
||||
gRfuStatic
|
||||
gRfuFixed
|
||||
gUnknown_3007470
|
||||
@@ -25,4 +25,3 @@ _Str_RFU_MBOOT::
|
||||
.align 2
|
||||
_Str_Sio32ID::
|
||||
.asciz "NINTENDOSio32ID_030820"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef GUARD_CONSTANTS_TRADE_H
|
||||
#ifndef GUARD_CONSTANTS_TRADE_H
|
||||
#define GUARD_CONSTANTS_TRADE_H
|
||||
|
||||
// TODO: document trade.c and trade_scene.c with corresponding macros
|
||||
@@ -115,4 +115,4 @@
|
||||
#define CANT_REGISTER_MON 1
|
||||
#define CANT_REGISTER_EGG 2
|
||||
|
||||
#endif //GUARD_CONSTANTS_TRADE_H
|
||||
#endif // GUARD_CONSTANTS_TRADE_H
|
||||
|
||||
@@ -461,7 +461,7 @@
|
||||
#define REG_DMA3CNT_L (*(vu16 *)REG_ADDR_DMA3CNT_L)
|
||||
#define REG_DMA3CNT_H (*(vu16 *)REG_ADDR_DMA3CNT_H)
|
||||
|
||||
#define REG_TMCNT(n) (*(vu16 *)(REG_ADDR_TMCNT + ((n) * 4)))
|
||||
#define REG_TMCNT(n) (*(vu32 *)(REG_ADDR_TMCNT + ((n) * 4)))
|
||||
#define REG_TMCNT_L(n) (*(vu16 *)(REG_ADDR_TMCNT_L + ((n) * 4)))
|
||||
#define REG_TMCNT_H(n) (*(vu16 *)(REG_ADDR_TMCNT_H + ((n) * 4)))
|
||||
#define REG_TM0CNT (*(vu32 *)REG_ADDR_TM0CNT)
|
||||
|
||||
@@ -45,4 +45,6 @@ void RLUnCompVram(const void *src, void *dest);
|
||||
|
||||
int MultiBoot(struct MultiBootParam *mp);
|
||||
|
||||
s32 Div(s32 num, s32 denom);
|
||||
|
||||
#endif // GUARD_GBA_SYSCALL_H
|
||||
|
||||
+19
-18
@@ -117,7 +117,7 @@ struct RfuStruct
|
||||
u8 unk_16;
|
||||
u8 unk_17;
|
||||
void (*callbackM)();
|
||||
void (*callbackS)();
|
||||
void (*callbackS)(u16);
|
||||
u32 callbackId;
|
||||
union RfuPacket *txPacket;
|
||||
union RfuPacket *rxPacket;
|
||||
@@ -145,7 +145,7 @@ struct RfuSlotStatusUNI
|
||||
u8 newDataFlag;
|
||||
u8 dataBlockFlag;
|
||||
void *recvBuffer;
|
||||
u32 recvBuffSize;
|
||||
u32 recvBufferSize;
|
||||
};
|
||||
|
||||
struct NIComm
|
||||
@@ -178,12 +178,12 @@ struct RfuSlotStatusNI
|
||||
|
||||
struct RfuFixed
|
||||
{
|
||||
void *reqCallback;
|
||||
void (*reqCallback)(u16, u16);
|
||||
void *fastCopyPtr;
|
||||
u16 fastCopyBuffer[24];
|
||||
u32 fastCopyBuffer2[12];
|
||||
u32 LLFBuffer[29];
|
||||
u8 *STWIBuffer;
|
||||
struct RfuIntrStruct *STWIBuffer;
|
||||
};
|
||||
|
||||
struct RfuStatic
|
||||
@@ -204,7 +204,7 @@ struct RfuStatic
|
||||
u16 unk_1a;
|
||||
u16 reqResult;
|
||||
u16 tryPid;
|
||||
u32 watchdogTimer;
|
||||
u16 watchdogTimer;
|
||||
u32 totalPacketSize;
|
||||
};
|
||||
|
||||
@@ -230,7 +230,7 @@ struct RfuLinkStatus
|
||||
u8 getNameFlag;
|
||||
u8 findParentCount;
|
||||
u8 watchInterval;
|
||||
u8 stength[4];
|
||||
u8 strength[4];
|
||||
u8 LLFReadyFlag;
|
||||
u8 remainLLFrameSizeParent;
|
||||
u8 remainLLFrameSizeChild[4];
|
||||
@@ -266,19 +266,20 @@ extern struct RfuSlotStatusNI *gRfuSlotStatusNI[4];
|
||||
extern struct RfuSlotStatusUNI *gRfuSlotStatusUNI[4];
|
||||
extern struct Unk_3007470 gUnknown_3007470;
|
||||
|
||||
// librfu_rfu
|
||||
void rfu_STC_clearAPIVariables(void);
|
||||
void rfu_REQ_stopMode(void);
|
||||
void rfu_waitREQComplete(void);
|
||||
u32 rfu_REQBN_softReset_and_checkID(void);
|
||||
u16 rfu_waitREQComplete(void);
|
||||
s32 rfu_REQBN_softReset_and_checkID(void);
|
||||
void rfu_REQ_sendData(u8);
|
||||
void rfu_setMSCCallback(void (*func)(u16));
|
||||
void rfu_setREQCallback(void (*func)(u16, u16));
|
||||
bool8 rfu_getMasterSlave(void);
|
||||
void rfu_REQBN_watchLink(u16 a0, u8 *a1, u8 *a2, u8 *a3);
|
||||
bool16 rfu_syncVBlank(void);
|
||||
u16 rfu_syncVBlank(void);
|
||||
void rfu_REQ_reset(void);
|
||||
void rfu_REQ_configSystem(u16, u8, u8);
|
||||
void rfu_REQ_configGameData(u8, u16, struct UnkLinkRfuStruct_02022B14 *, u8 *);
|
||||
void rfu_REQ_configGameData(u8 r6, u16 r2, const u8 *r4, const u8 *r7);
|
||||
void rfu_REQ_startSearchChild(void);
|
||||
void rfu_REQ_pollSearchChild(void);
|
||||
void rfu_REQ_endSearchChild(void);
|
||||
@@ -293,18 +294,18 @@ void rfu_REQ_CHILD_pollConnectRecovery(void);
|
||||
void rfu_REQ_CHILD_endConnectRecovery(void);
|
||||
void rfu_REQ_changeMasterSlave(void);
|
||||
void rfu_REQ_RFUStatus(void);
|
||||
void rfu_getRFUStatus(u8 *status);
|
||||
u8 *rfu_getSTWIRecvBuffer(void);
|
||||
u32 rfu_getRFUStatus(u8 *status);
|
||||
struct RfuIntrStruct *rfu_getSTWIRecvBuffer(void);
|
||||
u8 rfu_NI_CHILD_setSendGameName(u8 a0, u8 a1);
|
||||
void rfu_clearSlot(u8 a0, u8 a1);
|
||||
void rfu_clearAllSlot(void);
|
||||
bool16 rfu_CHILD_getConnectRecoveryStatus(u8 *status);
|
||||
bool16 rfu_getConnectParentStatus(u8 *status, u8 *a1);
|
||||
u16 rfu_getConnectParentStatus(u8 *status, u8 *a1);
|
||||
bool16 rfu_UNI_PARENT_getDRAC_ACK(u8 *a0);
|
||||
void rfu_REQ_disconnect(u8 who);
|
||||
void rfu_changeSendTarget(u8 a0, u8 who, u8 a2);
|
||||
void rfu_NI_stopReceivingData(u8 who);
|
||||
u16 rfu_initializeAPI(u32 *unk0, u16 unk1, IntrFunc *interrupt, bool8 copyInterruptToRam);
|
||||
u16 rfu_initializeAPI(struct Unk_3001190 *unk0, u16 unk1, IntrFunc *interrupt, bool8 copyInterruptToRam);
|
||||
void rfu_setTimerInterrupt(u8 which, IntrFunc *intr);
|
||||
void rfu_setRecvBuffer(u8 a0, u8 a1, void *a2, size_t a3);
|
||||
bool16 rfu_UNI_setSendData(u8 flag, void *ptr, u8 size);
|
||||
@@ -318,12 +319,12 @@ void rfu_NI_setSendData(u8, u8, const void *, u32);
|
||||
void IntrSIO32(void);
|
||||
|
||||
// librfu_stwi
|
||||
void STWI_init_all(struct RfuIntrStruct * interruptStruct, IntrFunc *interrupt, bool8 copyInterruptToRam);
|
||||
void STWI_init_all(struct RfuIntrStruct *interruptStruct, IntrFunc *interrupt, bool8 copyInterruptToRam);
|
||||
void STWI_set_MS_mode(u8 mode);
|
||||
void STWI_init_Callback_M(void);
|
||||
void STWI_init_Callback_S(void);
|
||||
void STWI_set_Callback_M(void (*callbackM)());
|
||||
void STWI_set_Callback_S(void (*callbackS)());
|
||||
void STWI_set_Callback_M(void *callbackM);
|
||||
void STWI_set_Callback_S(void (*callbackS)(u16));
|
||||
void STWI_init_timer(IntrFunc *interrupt, s32 timerSelect);
|
||||
void AgbRFU_SoftReset(void);
|
||||
void STWI_set_Callback_ID(u32 id);
|
||||
@@ -333,7 +334,7 @@ void STWI_send_DataRxREQ(void);
|
||||
void STWI_send_MS_ChangeREQ(void);
|
||||
void STWI_send_StopModeREQ(void);
|
||||
void STWI_send_SystemStatusREQ(void);
|
||||
void STWI_send_GameConfigREQ(u8 *unk1, u8 *data);
|
||||
void STWI_send_GameConfigREQ(const u8 *unk1, const u8 *data);
|
||||
void STWI_send_ResetREQ(void);
|
||||
void STWI_send_LinkStatusREQ(void);
|
||||
void STWI_send_VersionStatusREQ(void);
|
||||
|
||||
@@ -319,6 +319,7 @@ SECTIONS {
|
||||
src/agb_flash_le.o(.text);
|
||||
src/librfu_stwi.o(.text);
|
||||
asm/librfu_intr.o(.text);
|
||||
src/librfu_rfu.o(.text);
|
||||
asm/librfu.o(.text);
|
||||
src/isagbprn.o(.text);
|
||||
asm/libagbsyscall.o(.text);
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ u16 SetFlashTimerIntr(u8 timerNum, void (**intrFunc)(void))
|
||||
return 1;
|
||||
|
||||
sTimerNum = timerNum;
|
||||
sTimerReg = ®_TMCNT(sTimerNum);
|
||||
sTimerReg = ®_TMCNT_L(sTimerNum);
|
||||
*intrFunc = FlashTimerIntr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,736 @@
|
||||
#include "global.h"
|
||||
#include "librfu.h"
|
||||
|
||||
void rfu_CB_defaultCallback(u8, u16);
|
||||
void rfu_CB_reset(u8, u16);
|
||||
void rfu_CB_configGameData(u8, u16);
|
||||
void rfu_CB_stopMode(u8, u16);
|
||||
void rfu_CB_startSearchChild(u8, u16);
|
||||
void rfu_CB_pollAndEndSearchChild(u8, u16);
|
||||
void rfu_CB_startSearchParent(u8, u16);
|
||||
void rfu_CB_pollSearchParent(u8, u16);
|
||||
void rfu_CB_pollConnectParent(u8, u16);
|
||||
void rfu_CB_pollConnectParent(u8, u16);
|
||||
s32 sub_81E349C(u8);
|
||||
void rfu_enableREQCallback(bool8);
|
||||
void rfu_STC_readChildList(void);
|
||||
void rfu_STC_readParentCandidateList(void);
|
||||
void rfu_STC_REQ_callback(u8, u16);
|
||||
void rfu_STC_removeLinkData(u8, u8);
|
||||
void rfu_STC_fastCopy(u8 **a1, u8 **a2, s32 a3);
|
||||
void rfu_STC_clearLinkStatus(u8);
|
||||
void rfu_NI_checkCommFailCounter(void);
|
||||
|
||||
extern const char _Str_RFU_MBOOT[];
|
||||
|
||||
struct RfuStruct *gRfuState;
|
||||
ALIGNED(8) struct RfuSlotStatusUNI *gRfuSlotStatusUNI[4];
|
||||
struct RfuSlotStatusNI *gRfuSlotStatusNI[4];
|
||||
struct RfuLinkStatus *gRfuLinkStatus;
|
||||
struct RfuStatic *gRfuStatic;
|
||||
struct RfuFixed *gRfuFixed;
|
||||
ALIGNED(8) struct Unk_3007470 gUnknown_3007470;
|
||||
|
||||
u16 rfu_initializeAPI(struct Unk_3001190 *unk0, u16 unk1, IntrFunc *interrupt, bool8 copyInterruptToRam)
|
||||
{
|
||||
u16 i;
|
||||
u16 *dst;
|
||||
const u16 *src;
|
||||
u16 r3;
|
||||
|
||||
// is in EWRAM?
|
||||
if (((u32)unk0 & 0xF000000) == 0x2000000 && copyInterruptToRam)
|
||||
return 2;
|
||||
// is not 4-byte aligned?
|
||||
if ((u32)unk0 & 3)
|
||||
return 2;
|
||||
// Nintendo pls, just use a ternary for once
|
||||
if (copyInterruptToRam)
|
||||
{
|
||||
// An assert/debug print may have existed before, ie
|
||||
// printf("%s %u < %u", "somefile.c:12345", unk1, num)
|
||||
// to push this into r3?
|
||||
r3 = 0xe64;
|
||||
if (unk1 < r3)
|
||||
return 1;
|
||||
}
|
||||
if (!copyInterruptToRam)
|
||||
{
|
||||
r3 = 0x504; // same as above, this should be r3 not r0
|
||||
if (unk1 < r3)
|
||||
return 1;
|
||||
}
|
||||
gRfuLinkStatus = &unk0->linkStatus;
|
||||
gRfuStatic = &unk0->static_;
|
||||
gRfuFixed = &unk0->fixed;
|
||||
gRfuSlotStatusNI[0] = &unk0->NI[0];
|
||||
gRfuSlotStatusUNI[0] = &unk0->UNI[0];
|
||||
for (i = 1; i < NELEMS(gRfuSlotStatusNI); ++i)
|
||||
{
|
||||
gRfuSlotStatusNI[i] = &gRfuSlotStatusNI[i - 1][1];
|
||||
gRfuSlotStatusUNI[i] = &gRfuSlotStatusUNI[i - 1][1];
|
||||
}
|
||||
// TODO: Is it possible to fix the following 2 statements?
|
||||
// It's equivalent to:
|
||||
// gRfuFixed->STWIBuffer = &unk0->intr;
|
||||
// STWI_init_all(&unk0->intr, interrupt, copyInterruptToRam);
|
||||
gRfuFixed->STWIBuffer = (struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1];
|
||||
STWI_init_all((struct RfuIntrStruct *)&gRfuSlotStatusUNI[3][1], interrupt, copyInterruptToRam);
|
||||
rfu_STC_clearAPIVariables();
|
||||
for (i = 0; i < NELEMS(gRfuSlotStatusNI); ++i)
|
||||
{
|
||||
gRfuSlotStatusNI[i]->recvBuffer = 0;
|
||||
gRfuSlotStatusNI[i]->recvBufferSize = 0;
|
||||
gRfuSlotStatusUNI[i]->recvBuffer = 0;
|
||||
gRfuSlotStatusUNI[i]->recvBufferSize = 0;
|
||||
}
|
||||
src = (const u16 *)((u32)&rfu_STC_fastCopy & ~1);
|
||||
dst = gRfuFixed->fastCopyBuffer;
|
||||
// rfu_REQ_changeMasterSlave is the function next to rfu_STC_fastCopy
|
||||
for (r3 = ((void *)rfu_REQ_changeMasterSlave - (void *)rfu_STC_fastCopy) / sizeof(u16), --r3; r3 != 0xFFFF; --r3)
|
||||
*dst++ = *src++;
|
||||
gRfuFixed->fastCopyPtr = (u8 *)gRfuFixed->fastCopyBuffer + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rfu_STC_clearAPIVariables(void)
|
||||
{
|
||||
u16 IMEBackup = REG_IME;
|
||||
u8 i, r4;
|
||||
|
||||
REG_IME = 0;
|
||||
r4 = gRfuStatic->flags;
|
||||
CpuFill16(0, gRfuStatic, sizeof(struct RfuStatic));
|
||||
gRfuStatic->flags = r4 & 8;
|
||||
CpuFill16(0, gRfuLinkStatus, sizeof(struct RfuLinkStatus));
|
||||
gRfuLinkStatus->watchInterval = 4;
|
||||
gRfuStatic->nowWatchInterval = 0;
|
||||
gRfuLinkStatus->connMode = 0xFF;
|
||||
rfu_clearAllSlot();
|
||||
gRfuStatic->SCStartFlag = 0;
|
||||
for (i = 0; i < NELEMS(gRfuStatic->cidBak); ++i)
|
||||
gRfuStatic->cidBak[i] = 0;
|
||||
REG_IME = IMEBackup;
|
||||
}
|
||||
|
||||
void rfu_REQ_PARENT_resumeRetransmitAndChange(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_STC_REQ_callback);
|
||||
STWI_send_ResumeRetransmitAndChangeREQ();
|
||||
}
|
||||
|
||||
u16 rfu_UNI_PARENT_getDRAC_ACK(u8 *ackFlag)
|
||||
{
|
||||
struct RfuIntrStruct *buf;
|
||||
*ackFlag = 0;
|
||||
if (gRfuLinkStatus->connMode != 1)
|
||||
return 0x300;
|
||||
buf = rfu_getSTWIRecvBuffer();
|
||||
switch (buf->rxPacketAlloc.rfuPacket8.data[0])
|
||||
{
|
||||
case 40:
|
||||
case 54:
|
||||
if (buf->rxPacketAlloc.rfuPacket8.data[1] == 0)
|
||||
*ackFlag = gRfuLinkStatus->connSlotFlag;
|
||||
else
|
||||
*ackFlag = buf->rxPacketAlloc.rfuPacket8.data[4];
|
||||
return 0;
|
||||
default:
|
||||
return 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
void rfu_setTimerInterrupt(u8 which, IntrFunc *intr)
|
||||
{
|
||||
STWI_init_timer(intr, which);
|
||||
}
|
||||
|
||||
struct RfuIntrStruct *rfu_getSTWIRecvBuffer(void)
|
||||
{
|
||||
return gRfuFixed->STWIBuffer;
|
||||
}
|
||||
|
||||
void rfu_setMSCCallback(void (*callback)(u16))
|
||||
{
|
||||
STWI_set_Callback_S(callback);
|
||||
}
|
||||
|
||||
void rfu_setREQCallback(void (*callback)(u16, u16))
|
||||
{
|
||||
gRfuFixed->reqCallback = callback;
|
||||
rfu_enableREQCallback(callback != NULL);
|
||||
}
|
||||
|
||||
void rfu_enableREQCallback(bool8 enable)
|
||||
{
|
||||
if (enable)
|
||||
gRfuStatic->flags |= 8;
|
||||
else
|
||||
gRfuStatic->flags &= 0xF7;
|
||||
}
|
||||
|
||||
void rfu_STC_REQ_callback(u8 r5, u16 reqResult)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_defaultCallback);
|
||||
gRfuStatic->reqResult = reqResult;
|
||||
if (gRfuStatic->flags & 8)
|
||||
gRfuFixed->reqCallback(r5, reqResult);
|
||||
}
|
||||
|
||||
void rfu_CB_defaultCallback(u8 r0, u16 reqResult)
|
||||
{
|
||||
s32 r5;
|
||||
u8 i;
|
||||
|
||||
if (r0 == 0xFF)
|
||||
{
|
||||
if (gRfuStatic->flags & 8)
|
||||
gRfuFixed->reqCallback(r0, reqResult);
|
||||
r5 = gRfuLinkStatus->connSlotFlag | gRfuLinkStatus->linkLossSlotFlag;
|
||||
for (i = 0; i < 4; ++i)
|
||||
if ((r5 >> i) & 1)
|
||||
rfu_STC_removeLinkData(i, 1);
|
||||
gRfuLinkStatus->connMode = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
u16 rfu_waitREQComplete(void)
|
||||
{
|
||||
STWI_poll_CommandEnd();
|
||||
return gRfuStatic->reqResult;
|
||||
}
|
||||
|
||||
void rfu_REQ_RFUStatus(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_STC_REQ_callback);
|
||||
STWI_send_SystemStatusREQ();
|
||||
}
|
||||
|
||||
u32 rfu_getRFUStatus(u8 *status)
|
||||
{
|
||||
if (gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[0] != 0x93)
|
||||
return 0x10;
|
||||
if (STWI_poll_CommandEnd() == 0)
|
||||
*status = gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[7];
|
||||
else
|
||||
*status = 0xFF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 rfu_MBOOT_CHILD_inheritanceLinkStatus(void)
|
||||
{
|
||||
const char *s1 = _Str_RFU_MBOOT;
|
||||
char *s2 = (char *)0x30000F0;
|
||||
u16 checksum;
|
||||
u16 *r2;
|
||||
u8 i;
|
||||
|
||||
while (*s1 != '\0')
|
||||
if (*s1++ != *s2++)
|
||||
return 1;
|
||||
r2 = (u16 *)0x3000000;
|
||||
checksum = 0;
|
||||
for (i = 0; i < 90; ++i)
|
||||
checksum += *r2++;
|
||||
if (checksum != *(u16 *)0x30000FA)
|
||||
return 1;
|
||||
CpuCopy16((u16 *)0x3000000, gRfuLinkStatus, sizeof(struct RfuLinkStatus));
|
||||
gRfuStatic->flags |= 0x80;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rfu_REQ_stopMode(void)
|
||||
{
|
||||
vu32 *timerReg;
|
||||
|
||||
if (REG_IME == 0)
|
||||
{
|
||||
rfu_STC_REQ_callback(61, 6);
|
||||
gRfuState->error = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
AgbRFU_SoftReset();
|
||||
rfu_STC_clearAPIVariables();
|
||||
if (sub_81E349C(8) == 0x8001)
|
||||
{
|
||||
timerReg = ®_TMCNT(gRfuState->timerSelect);
|
||||
*timerReg = 0;
|
||||
*timerReg = (TIMER_ENABLE | TIMER_1024CLK) << 16;
|
||||
while (*timerReg << 16 < 262 << 16)
|
||||
;
|
||||
*timerReg = 0;
|
||||
STWI_set_Callback_M(rfu_CB_stopMode);
|
||||
STWI_send_StopModeREQ();
|
||||
}
|
||||
else
|
||||
{
|
||||
REG_SIOCNT = SIO_MULTI_MODE;
|
||||
rfu_STC_REQ_callback(61, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rfu_CB_stopMode(u8 a1, u16 reqResult)
|
||||
{
|
||||
if (reqResult == 0)
|
||||
REG_SIOCNT = SIO_MULTI_MODE;
|
||||
rfu_STC_REQ_callback(a1, reqResult);
|
||||
}
|
||||
|
||||
s32 rfu_REQBN_softReset_and_checkID(void)
|
||||
{
|
||||
s32 r2;
|
||||
|
||||
if (REG_IME == 0)
|
||||
return -1;
|
||||
AgbRFU_SoftReset();
|
||||
rfu_STC_clearAPIVariables();
|
||||
if ((r2 = sub_81E349C(30)) == 0)
|
||||
REG_SIOCNT = SIO_MULTI_MODE;
|
||||
return r2;
|
||||
}
|
||||
|
||||
void rfu_REQ_reset(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_reset);
|
||||
STWI_send_ResetREQ();
|
||||
}
|
||||
|
||||
void rfu_CB_reset(u8 a1, u16 reqResult)
|
||||
{
|
||||
if (reqResult == 0)
|
||||
rfu_STC_clearAPIVariables();
|
||||
rfu_STC_REQ_callback(a1, reqResult);
|
||||
}
|
||||
|
||||
void rfu_REQ_configSystem(u16 r4, u8 r5, u8 r6)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_STC_REQ_callback);
|
||||
STWI_send_SystemConfigREQ((r4 & 3) | 0x3C, r5, r6);
|
||||
if (r6 == 0)
|
||||
{
|
||||
gRfuStatic->unk_1a = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 IMEBackup = REG_IME;
|
||||
|
||||
REG_IME = 0;
|
||||
gRfuStatic->unk_1a = Div(600, r6);
|
||||
REG_IME = IMEBackup;
|
||||
}
|
||||
}
|
||||
|
||||
void rfu_REQ_configGameData(u8 r6, u16 r2, const u8 *r4, const u8 *r7)
|
||||
{
|
||||
u8 sp[16];
|
||||
u8 i;
|
||||
u8 r3;
|
||||
const u8 *r5 = r4;
|
||||
const u8 *r1;
|
||||
|
||||
sp[0] = r2;
|
||||
sp[1] = r2 >> 8;
|
||||
if (r6 != 0)
|
||||
sp[1] = (r2 >> 8) | 0x80;
|
||||
for (i = 2; i < 15; ++i)
|
||||
sp[i] = *r4++;
|
||||
r3 = 0;
|
||||
r1 = r7;
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
r3 += *r1++;
|
||||
r3 += *r5++;
|
||||
}
|
||||
sp[15] = ~r3;
|
||||
if (r6 != 0)
|
||||
sp[14] = 0;
|
||||
STWI_set_Callback_M(rfu_CB_configGameData);
|
||||
STWI_send_GameConfigREQ(sp, r7);
|
||||
}
|
||||
|
||||
void rfu_CB_configGameData(u8 ip, u16 r7)
|
||||
{
|
||||
s32 r2, r3;
|
||||
u8 *r4;
|
||||
u8 i;
|
||||
u8 *r1;
|
||||
|
||||
if (r7 == 0)
|
||||
{
|
||||
r1 = gRfuState->txPacket->rfuPacket8.data;
|
||||
r2 = gRfuLinkStatus->my.serialNum = r1[4];
|
||||
gRfuLinkStatus->my.serialNum = (r1[5] << 8) | r2;
|
||||
r4 = &r1[6];
|
||||
if (gRfuLinkStatus->my.serialNum & 0x8000)
|
||||
{
|
||||
gRfuLinkStatus->my.serialNum = gRfuLinkStatus->my.serialNum ^ 0x8000;
|
||||
gRfuLinkStatus->my.multibootFlag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gRfuLinkStatus->my.multibootFlag = 0;
|
||||
}
|
||||
for (i = 0; i < NELEMS(gRfuLinkStatus->my.gname) - 2; ++i)
|
||||
gRfuLinkStatus->my.gname[i] = *r4++;
|
||||
++r4;
|
||||
for (i = 0; i < NELEMS(gRfuLinkStatus->my.uname) - 1; ++i)
|
||||
gRfuLinkStatus->my.uname[i] = *r4++;
|
||||
}
|
||||
rfu_STC_REQ_callback(ip, r7);
|
||||
}
|
||||
|
||||
void rfu_REQ_startSearchChild(void)
|
||||
{
|
||||
u16 r1;
|
||||
|
||||
STWI_set_Callback_M(rfu_CB_defaultCallback);
|
||||
STWI_send_SystemStatusREQ();
|
||||
r1 = STWI_poll_CommandEnd();
|
||||
if (r1 == 0)
|
||||
{
|
||||
if (gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[7] == 0)
|
||||
rfu_STC_clearLinkStatus(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
rfu_STC_REQ_callback(25, r1);
|
||||
}
|
||||
STWI_set_Callback_M(rfu_CB_startSearchChild);
|
||||
STWI_send_SC_StartREQ();
|
||||
}
|
||||
|
||||
void rfu_CB_startSearchChild(u8 r3, u16 reqResult)
|
||||
{
|
||||
if (reqResult == 0)
|
||||
gRfuStatic->SCStartFlag = 1;
|
||||
rfu_STC_REQ_callback(r3, reqResult);
|
||||
}
|
||||
|
||||
void rfu_STC_clearLinkStatus(u8 r4)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
rfu_clearAllSlot();
|
||||
if (r4 != 0)
|
||||
{
|
||||
CpuFill16(0, gRfuLinkStatus->partner, sizeof(gRfuLinkStatus->partner));
|
||||
gRfuLinkStatus->findParentCount = 0;
|
||||
}
|
||||
for (i = 0; i < NELEMS(gRfuLinkStatus->strength); ++i)
|
||||
gRfuLinkStatus->strength[i] = 0;
|
||||
gRfuLinkStatus->connCount = 0;
|
||||
gRfuLinkStatus->connSlotFlag = 0;
|
||||
gRfuLinkStatus->linkLossSlotFlag = 0;
|
||||
gRfuLinkStatus->getNameFlag = 0;
|
||||
}
|
||||
|
||||
void rfu_REQ_pollSearchChild(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_pollAndEndSearchChild);
|
||||
STWI_send_SC_PollingREQ();
|
||||
}
|
||||
|
||||
void rfu_REQ_endSearchChild(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_pollAndEndSearchChild);
|
||||
STWI_send_SC_EndREQ();
|
||||
}
|
||||
|
||||
void rfu_CB_pollAndEndSearchChild(u8 r4, u16 reqResult)
|
||||
{
|
||||
if (reqResult == 0)
|
||||
rfu_STC_readChildList();
|
||||
if (r4 == 26)
|
||||
{
|
||||
if (gRfuLinkStatus->my.id == 0)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_defaultCallback);
|
||||
STWI_send_SystemStatusREQ();
|
||||
if (STWI_poll_CommandEnd() == 0)
|
||||
gRfuLinkStatus->my.id = *(u16 *)&gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket32.data[0];
|
||||
}
|
||||
}
|
||||
else if (r4 == 27)
|
||||
{
|
||||
if (gRfuLinkStatus->connMode == 255)
|
||||
gRfuLinkStatus->my.id = 0;
|
||||
gRfuStatic->SCStartFlag = 0;
|
||||
}
|
||||
rfu_STC_REQ_callback(r4, reqResult);
|
||||
}
|
||||
|
||||
void rfu_STC_readChildList(void)
|
||||
{
|
||||
u32 r5;
|
||||
u8 r8 = gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[1];
|
||||
u8 *r4;
|
||||
u8 i;
|
||||
u8 sp[4];
|
||||
u8 r2;
|
||||
|
||||
if (r8 != 0)
|
||||
{
|
||||
r5 = gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket32.data[0];
|
||||
STWI_set_Callback_M(rfu_CB_defaultCallback);
|
||||
STWI_send_LinkStatusREQ();
|
||||
if (STWI_poll_CommandEnd() == 0)
|
||||
{
|
||||
r4 = &gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[4];
|
||||
for (i = 0; i < NELEMS(sp); ++i)
|
||||
sp[i] = *r4++;
|
||||
}
|
||||
gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket32.data[0] = r5;
|
||||
}
|
||||
for (r4 = &gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[4];
|
||||
r8 != 0;
|
||||
r4 += 4)
|
||||
{
|
||||
r2 = r4[2];
|
||||
if (r2 < 4 && !((gRfuLinkStatus->connSlotFlag >> r2) & 1) && !((gRfuLinkStatus->linkLossSlotFlag >> r2) & 1))
|
||||
{
|
||||
if (sp[r2] != 0)
|
||||
++gRfuStatic->lsFixedCount[r2];
|
||||
if (gRfuStatic->lsFixedCount[r2] >= 4)
|
||||
{
|
||||
gRfuStatic->lsFixedCount[r2] = 0;
|
||||
gRfuLinkStatus->strength[r2] = 0xFF;
|
||||
gRfuLinkStatus->connSlotFlag |= 1 << r2;
|
||||
++gRfuLinkStatus->connCount;
|
||||
gRfuLinkStatus->partner[r2].id = *(u16 *)r4;
|
||||
gRfuLinkStatus->partner[r2].slot = r2;
|
||||
gRfuLinkStatus->connMode = 1;
|
||||
gRfuStatic->flags &= 0x7F;
|
||||
gRfuStatic->cidBak[r2] = gRfuLinkStatus->partner[r2].id;
|
||||
}
|
||||
}
|
||||
--r8;
|
||||
}
|
||||
}
|
||||
|
||||
void rfu_REQ_startSearchParent(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_startSearchParent);
|
||||
STWI_send_SP_StartREQ();
|
||||
}
|
||||
|
||||
void rfu_CB_startSearchParent(u8 r5, u16 reqResult)
|
||||
{
|
||||
if (reqResult == 0)
|
||||
rfu_STC_clearLinkStatus(0);
|
||||
rfu_STC_REQ_callback(r5, reqResult);
|
||||
}
|
||||
|
||||
void rfu_REQ_pollSearchParent(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_pollSearchParent);
|
||||
STWI_send_SP_PollingREQ();
|
||||
}
|
||||
|
||||
void rfu_CB_pollSearchParent(u8 r5, u16 reqResult)
|
||||
{
|
||||
if (reqResult == 0)
|
||||
rfu_STC_readParentCandidateList();
|
||||
rfu_STC_REQ_callback(r5, reqResult);
|
||||
}
|
||||
|
||||
void rfu_REQ_endSearchParent(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_STC_REQ_callback);
|
||||
STWI_send_SP_EndREQ();
|
||||
}
|
||||
|
||||
void rfu_STC_readParentCandidateList(void)
|
||||
{
|
||||
u8 r7, r6, r5, r4, r3;
|
||||
u8 *r1, *r2;
|
||||
struct RfuTgtData *r4_;
|
||||
|
||||
CpuFill16(0, gRfuLinkStatus->partner, sizeof(gRfuLinkStatus->partner));
|
||||
r2 = &gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[0];
|
||||
r7 = r2[1];
|
||||
r2 += 4;
|
||||
gRfuLinkStatus->findParentCount = 0;
|
||||
for (r6 = 0; r6 < 4 && r7 != 0; ++r6)
|
||||
{
|
||||
r7 -= 7;
|
||||
r1 = r2 + 6;
|
||||
r2 += 19;
|
||||
r5 = ~*r2;
|
||||
++r2;
|
||||
r4 = 0;
|
||||
for (r3 = 0; r3 < 8; ++r3)
|
||||
{
|
||||
r4 += *r2++;
|
||||
r4 += *r1++;
|
||||
}
|
||||
if (r4 == r5)
|
||||
{
|
||||
r2 -= 28;
|
||||
r4_ = &gRfuLinkStatus->partner[gRfuLinkStatus->findParentCount];
|
||||
r4_->id = *(u16 *)r2;
|
||||
r2 += 2;
|
||||
r4_->slot = *r2;
|
||||
r2 += 2;
|
||||
r4_->serialNum = *(u16 *)r2 & 0x7FFF;
|
||||
if (*(u16 *)r2 & 0x8000)
|
||||
r4_->multibootFlag = 1;
|
||||
else
|
||||
r4_->multibootFlag = 0;
|
||||
r2 += 2;
|
||||
for (r3 = 0; r3 < NELEMS(r4_->gname) - 2; ++r3)
|
||||
r4_->gname[r3] = *r2++;
|
||||
++r2;
|
||||
for (r3 = 0; r3 < NELEMS(r4_->uname) - 1; ++r3)
|
||||
r4_->uname[r3] = *r2++;
|
||||
++gRfuLinkStatus->findParentCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rfu_REQ_startConnectParent(u16 r4)
|
||||
{
|
||||
u16 r3 = 0;
|
||||
u8 i;
|
||||
for (i = 0; i < NELEMS(gRfuLinkStatus->partner) && gRfuLinkStatus->partner[i].id != r4; ++i)
|
||||
;
|
||||
if (i == 4)
|
||||
r3 = 256;
|
||||
if (r3 == 0)
|
||||
{
|
||||
gRfuStatic->tryPid = r4;
|
||||
STWI_set_Callback_M(rfu_STC_REQ_callback);
|
||||
STWI_send_CP_StartREQ(r4);
|
||||
}
|
||||
else
|
||||
{
|
||||
rfu_STC_REQ_callback(31, r3);
|
||||
}
|
||||
}
|
||||
|
||||
void rfu_REQ_pollConnectParent(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_pollConnectParent);
|
||||
STWI_send_CP_PollingREQ();
|
||||
}
|
||||
|
||||
void rfu_CB_pollConnectParent(u8 sp24, u16 sp28)
|
||||
{
|
||||
u16 id;
|
||||
u8 slot;
|
||||
u8 r2, r5;
|
||||
struct RfuTgtData *r9;
|
||||
struct RfuTgtData sp;
|
||||
|
||||
if (sp28 == 0)
|
||||
{
|
||||
id = gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket32.data[0];
|
||||
slot = gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[6];
|
||||
if (gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[7] == 0)
|
||||
{
|
||||
r2 = 1 << slot;
|
||||
if (!(r2 & gRfuLinkStatus->connSlotFlag))
|
||||
{
|
||||
gRfuLinkStatus->connSlotFlag |= r2;
|
||||
gRfuLinkStatus->linkLossSlotFlag &= ~r2;
|
||||
gRfuLinkStatus->my.id = id;
|
||||
++gRfuLinkStatus->connCount;
|
||||
gRfuLinkStatus->connMode = 0;
|
||||
gRfuStatic->flags |= 0x80;
|
||||
for (r5 = 0; r5 < NELEMS(gRfuLinkStatus->partner); ++r5)
|
||||
{
|
||||
if (gRfuLinkStatus->partner[r5].id == gRfuStatic->tryPid)
|
||||
{
|
||||
if (gRfuLinkStatus->findParentCount != 0)
|
||||
{
|
||||
r9 = &sp;
|
||||
CpuCopy16(&gRfuLinkStatus->partner[r5], &sp, sizeof(struct RfuTgtData));
|
||||
CpuFill16(0, gRfuLinkStatus->partner, sizeof(gRfuLinkStatus->partner));
|
||||
gRfuLinkStatus->findParentCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
r9 = &gRfuLinkStatus->partner[r5];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r5 < 4)
|
||||
{
|
||||
CpuCopy16(r9, &gRfuLinkStatus->partner[slot], sizeof(struct RfuTgtData));
|
||||
gRfuLinkStatus->partner[slot].slot = slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rfu_STC_REQ_callback(sp24, sp28);
|
||||
}
|
||||
|
||||
u16 rfu_getConnectParentStatus(u8 *status, u8 *r1)
|
||||
{
|
||||
u8 r0, *r2;
|
||||
|
||||
*status = 0xFF;
|
||||
r2 = gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data;
|
||||
r0 = r2[0] + 96;
|
||||
if (r0 <= 1)
|
||||
{
|
||||
r2 += 6;
|
||||
*r1 = r2[0];
|
||||
*status = r2[1];
|
||||
return 0;
|
||||
}
|
||||
return 0x10;
|
||||
}
|
||||
|
||||
void rfu_REQ_endConnectParent(void)
|
||||
{
|
||||
STWI_set_Callback_M(rfu_CB_pollConnectParent);
|
||||
STWI_send_CP_EndREQ();
|
||||
if (gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[6] < 4)
|
||||
gRfuStatic->linkEmergencyFlag[gRfuFixed->STWIBuffer->rxPacketAlloc.rfuPacket8.data[6]] = 0;
|
||||
}
|
||||
|
||||
u16 rfu_syncVBlank(void)
|
||||
{
|
||||
u8 r3, r4;
|
||||
s32 r5;
|
||||
|
||||
rfu_NI_checkCommFailCounter();
|
||||
if (gRfuLinkStatus->connMode == 0xFF)
|
||||
return 0;
|
||||
if (gRfuStatic->nowWatchInterval != 0)
|
||||
--gRfuStatic->nowWatchInterval;
|
||||
r3 = rfu_getMasterSlave();
|
||||
if (!(gRfuStatic->flags & 2))
|
||||
{
|
||||
if (r3 == 0)
|
||||
{
|
||||
gRfuStatic->flags |= 4;
|
||||
gRfuStatic->watchdogTimer = 360;
|
||||
}
|
||||
}
|
||||
else if (r3 != 0)
|
||||
{
|
||||
gRfuStatic->flags &= 0xFB;
|
||||
}
|
||||
if (r3 != 0)
|
||||
gRfuStatic->flags &= 0xFD;
|
||||
else
|
||||
gRfuStatic->flags |= 2;
|
||||
if (!(gRfuStatic->flags & 4))
|
||||
return 0;
|
||||
if (gRfuStatic->watchdogTimer == 0)
|
||||
{
|
||||
gRfuStatic->flags &= 0xFB;
|
||||
r5 = gRfuLinkStatus->connSlotFlag | gRfuLinkStatus->linkLossSlotFlag;
|
||||
for (r4 = 0; r4 < 4; ++r4)
|
||||
if ((r5 >> r4) & 1)
|
||||
rfu_STC_removeLinkData(r4, 1);
|
||||
gRfuLinkStatus->connMode = 0xFF;
|
||||
return 1;
|
||||
}
|
||||
--gRfuStatic->watchdogTimer;
|
||||
return 0;
|
||||
}
|
||||
+11
-10
@@ -64,7 +64,7 @@ void AgbRFU_SoftReset(void)
|
||||
timerH = ®_TMCNT_H(gRfuState->timerSelect);
|
||||
*timerH = 0;
|
||||
*timerL = 0;
|
||||
*timerH = 0x83;
|
||||
*timerH = TIMER_ENABLE | TIMER_1024CLK;
|
||||
while (*timerL <= 0x11)
|
||||
REG_RCNT = 0x80A2;
|
||||
*timerH = 3;
|
||||
@@ -117,12 +117,13 @@ void STWI_init_Callback_S(void)
|
||||
STWI_set_Callback_S(NULL);
|
||||
}
|
||||
|
||||
void STWI_set_Callback_M(void (*callbackM)())
|
||||
// The callback can take 2 or 3 arguments.
|
||||
void STWI_set_Callback_M(void *callbackM)
|
||||
{
|
||||
gRfuState->callbackM = callbackM;
|
||||
}
|
||||
|
||||
void STWI_set_Callback_S(void (*callbackS)())
|
||||
void STWI_set_Callback_S(void (*callbackS)(u16))
|
||||
{
|
||||
gRfuState->callbackS = callbackS;
|
||||
}
|
||||
@@ -193,7 +194,7 @@ void STWI_send_ConfigStatusREQ(void)
|
||||
}
|
||||
}
|
||||
|
||||
void STWI_send_GameConfigREQ(u8 *unk1, u8 *data)
|
||||
void STWI_send_GameConfigREQ(const u8 *unk1, const u8 *data)
|
||||
{
|
||||
u8 *packetBytes;
|
||||
s32 i;
|
||||
@@ -496,7 +497,7 @@ static void STWI_intr_timer(void)
|
||||
gRfuState->timerActive = 1;
|
||||
STWI_stop_timer();
|
||||
STWI_reset_ClockCounter();
|
||||
if (gRfuState->callbackM)
|
||||
if (gRfuState->callbackM != NULL)
|
||||
gRfuState->callbackM(255, 0);
|
||||
break;
|
||||
}
|
||||
@@ -546,7 +547,7 @@ static u16 STWI_init(u8 request)
|
||||
if (!REG_IME)
|
||||
{
|
||||
gRfuState->error = 6;
|
||||
if (gRfuState->callbackM)
|
||||
if (gRfuState->callbackM != NULL)
|
||||
gRfuState->callbackM(request, gRfuState->error);
|
||||
return TRUE;
|
||||
}
|
||||
@@ -554,14 +555,14 @@ static u16 STWI_init(u8 request)
|
||||
{
|
||||
gRfuState->error = 2;
|
||||
gRfuState->unk_2c = FALSE;
|
||||
if (gRfuState->callbackM)
|
||||
if (gRfuState->callbackM != NULL)
|
||||
gRfuState->callbackM(request, gRfuState->error);
|
||||
return TRUE;
|
||||
}
|
||||
else if(!gRfuState->msMode)
|
||||
{
|
||||
gRfuState->error = 4;
|
||||
if (gRfuState->callbackM)
|
||||
if (gRfuState->callbackM != NULL)
|
||||
gRfuState->callbackM(request, gRfuState->error, gRfuState);
|
||||
return TRUE;
|
||||
}
|
||||
@@ -617,14 +618,14 @@ static s32 STWI_restart_Command(void)
|
||||
{
|
||||
gRfuState->error = 1;
|
||||
gRfuState->unk_2c = 0;
|
||||
if (gRfuState->callbackM)
|
||||
if (gRfuState->callbackM != NULL)
|
||||
gRfuState->callbackM(gRfuState->reqActiveCommand, gRfuState->error);
|
||||
}
|
||||
else
|
||||
{
|
||||
gRfuState->error = 1;
|
||||
gRfuState->unk_2c = 0;
|
||||
if (gRfuState->callbackM)
|
||||
if (gRfuState->callbackM != NULL)
|
||||
gRfuState->callbackM(gRfuState->reqActiveCommand, gRfuState->error);
|
||||
gRfuState->state = 4; // TODO: what's 4
|
||||
}
|
||||
|
||||
+2
-21
@@ -159,24 +159,5 @@ gUnknown_3005E94: @ 3005E94
|
||||
.include "berry_fix_program.o"
|
||||
.include "m4a.o"
|
||||
.include "agb_flash.o"
|
||||
|
||||
gRfuState: @ 3007438
|
||||
.space 0x8
|
||||
|
||||
gRfuSlotStatusUNI: @ 3007440
|
||||
.space 0x10
|
||||
|
||||
gRfuSlotStatusNI: @ 3007450
|
||||
.space 0x10
|
||||
|
||||
gRfuLinkStatus: @ 3007460
|
||||
.space 0x4
|
||||
|
||||
gRfuStatic: @ 3007464
|
||||
.space 0x4
|
||||
|
||||
gRfuFixed: @ 3007468
|
||||
.space 0x8
|
||||
|
||||
gUnknown_3007470: @ 3007470
|
||||
.space 0xC
|
||||
.align 2
|
||||
.include "librfu_rfu.o"
|
||||
|
||||
Reference in New Issue
Block a user