mirror of
https://github.com/HarbourMasters/Starship.git
synced 2025-02-02 16:23:57 +03:00
Audio and Message Fixes
This commit is contained in:
parent
11a148a047
commit
f07a9b6c81
@ -34,7 +34,7 @@ void Audio_PlayPauseSfx(u8 active);
|
||||
void Audio_PlayMapMenuSfx(u8 active);
|
||||
void Audio_KillAllSfx(void);
|
||||
|
||||
#define AUDIO_PLAY_SFX(sfxId, srcPos, token) //(Audio_PlaySfx((sfxId),(srcPos),(token),&gDefaultMod,&gDefaultMod,&gDefaultReverb))
|
||||
#define AUDIO_PLAY_SFX(sfxId, srcPos, token) (Audio_PlaySfx((sfxId),(srcPos),(token),&gDefaultMod,&gDefaultMod,&gDefaultReverb))
|
||||
|
||||
#define SFX_FLAG_18 (1 << 18) // makes distance ignore z position? probably more
|
||||
#define SFX_FLAG_19 (1 << 19)
|
||||
|
@ -692,7 +692,12 @@ void Audio_ProcessSeqCmd(u32 seqCmd) {
|
||||
seqNumber = seqCmd & 0xFF;
|
||||
seqArgs = (seqCmd & 0xFF00) >> 8;
|
||||
fadeTimer = (seqCmd & 0xFF0000) >> 13;
|
||||
if (!sActiveSequences[seqPlayId].isWaitingForFonts) {
|
||||
|
||||
seqArgs = 0;
|
||||
|
||||
//if (!sActiveSequences[seqPlayId].isWaitingForFonts)
|
||||
if (true)
|
||||
{
|
||||
if (seqArgs < 0x80) {
|
||||
Audio_StartSequence(seqPlayId, seqNumber, seqArgs, fadeTimer);
|
||||
} else {
|
||||
@ -888,12 +893,14 @@ void Audio_ProcessSeqCmd(u32 seqCmd) {
|
||||
oldSpecId = sAudioSpecId;
|
||||
sAudioSpecId = specId;
|
||||
|
||||
if (oldSpecId != specId) {
|
||||
if (oldSpecId != specId)
|
||||
{
|
||||
AudioThread_ResetAudioHeap(specId);
|
||||
Audio_StartReset(oldSpecId);
|
||||
AUDIOCMD_GLOBAL_STOP_AUDIOCMDS();
|
||||
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Audio_StopSequence(SEQ_PLAYER_BGM, 1);
|
||||
Audio_StopSequence(SEQ_PLAYER_FANFARE, 1);
|
||||
}
|
||||
@ -991,7 +998,8 @@ void Audio_UpdateActiveSequences(void) {
|
||||
|
||||
for (seqPlayId = 0; seqPlayId < SEQ_PLAYER_MAX; seqPlayId++) {
|
||||
if (sActiveSequences[seqPlayId].isWaitingForFonts) {
|
||||
switch ((s32) AudioThread_GetAsyncLoadStatus(&out)) {
|
||||
switch ((s32) AudioThread_GetAsyncLoadStatus(&out))
|
||||
{
|
||||
case SEQ_PLAYER_BGM + 1:
|
||||
case SEQ_PLAYER_FANFARE + 1:
|
||||
case SEQ_PLAYER_SFX + 1:
|
||||
@ -1322,7 +1330,7 @@ void Audio_ProcessSfxRequest(void) {
|
||||
SfxRequest* request = &sSfxRequests[sSfxRequestReadIndex];
|
||||
u8 next;
|
||||
s32 bankId;
|
||||
u8 evict;
|
||||
u8 evict = 0;
|
||||
u32 sfxId;
|
||||
u8 count;
|
||||
SfxBankEntry* entry;
|
||||
@ -1850,6 +1858,7 @@ void Audio_UpdateVoice(void) {
|
||||
voiceId = sNextVoiceId % 1000;
|
||||
voiceIdHi = voiceId / 256;
|
||||
voiceIdLo = voiceId % 256;
|
||||
|
||||
AUDIOCMD_CHANNEL_SET_IO(SEQ_PLAYER_VOICE, 15, 0, 1);
|
||||
AUDIOCMD_CHANNEL_SET_IO(SEQ_PLAYER_VOICE, 15, 4, voiceBank);
|
||||
AUDIOCMD_CHANNEL_SET_IO(SEQ_PLAYER_VOICE, 15, 5, voiceIdHi);
|
||||
@ -2570,6 +2579,7 @@ void Audio_SetBgmParam(s8 bgmParam) {
|
||||
}
|
||||
|
||||
void Audio_PlaySequence(u8 seqPlayId, u16 seqId, u8 fadeinTime, u8 bgmParam) {
|
||||
//seqId &= 0xFF;
|
||||
SEQCMD_SET_SEQPLAYER_IO(seqPlayId, 0, bgmParam);
|
||||
SEQCMD_PLAY_SEQUENCE(seqPlayId, fadeinTime, 0, seqId);
|
||||
}
|
||||
|
@ -451,7 +451,7 @@ uintptr_t AudioHeap_SearchCaches(s32 tableType, s32 cache, s32 id) {
|
||||
return (uintptr_t) ramAddr;
|
||||
}
|
||||
if (cache == CACHE_PERMANENT) {
|
||||
return (uintptr_t) NULL;
|
||||
//return (uintptr_t) NULL;
|
||||
}
|
||||
return (uintptr_t) AudioHeap_SearchRegularCaches(tableType, cache, id);
|
||||
}
|
||||
@ -667,9 +667,17 @@ void AudioHeap_Init(void) {
|
||||
|
||||
gNumNotes = spec->numNotes;
|
||||
D_8014C1B0 = spec->unk_14;
|
||||
gMaxTempo = (u16) ((gAudioBufferParams.ticksPerUpdate * 2880000.0f / gSeqTicksPerBeat) / gMaxTempoTvTypeFactors);
|
||||
|
||||
// STARTODO: The game was originally designed to use either 1 or 2 buffers depending on the scenario
|
||||
// Using 1 buffer has caused issues, so we hardcoded it to 2.
|
||||
// To prevent sequences from going too fast, we added a * 2 here.
|
||||
// This is not an optimal fix but it works. We may wish to find something better in the future.
|
||||
gMaxTempo = (u16) ((gAudioBufferParams.ticksPerUpdate * 2 * 2880000.0f / gSeqTicksPerBeat) / gMaxTempoTvTypeFactors);
|
||||
//gMaxTempo = (u16) ((gAudioBufferParams.ticksPerUpdate * 2880000.0f / gSeqTicksPerBeat) / gMaxTempoTvTypeFactors);
|
||||
|
||||
gAudioBufferParams.numBuffers = spec->numBuffers;
|
||||
gAudioBufferParams.numBuffers = 2;
|
||||
|
||||
gAudioBufferParams.samplesPerFrameTarget *= gAudioBufferParams.numBuffers;
|
||||
gAudioBufferParams.maxAiBufferLength *= gAudioBufferParams.numBuffers;
|
||||
gAudioBufferParams.minAiBufferLength *= gAudioBufferParams.numBuffers;
|
||||
|
@ -522,9 +522,12 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) {
|
||||
switch (tableType) {
|
||||
case SEQUENCE_TABLE:
|
||||
gSeqLoadStatus[id] = LOAD_STATUS_COMPLETE;
|
||||
|
||||
//ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, id);
|
||||
return Audio_LoadBlob(gAudioSeq, table->entries[id].romAddr);
|
||||
case FONT_TABLE:
|
||||
gFontLoadStatus[id] = LOAD_STATUS_COMPLETE;
|
||||
//ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, id);
|
||||
return Audio_LoadFont(table->entries[id]);
|
||||
case SAMPLE_TABLE:
|
||||
loadStatus = 0;
|
||||
@ -708,7 +711,7 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData,
|
||||
|
||||
switch (tableType) {
|
||||
case SEQUENCE_TABLE:
|
||||
gFontLoadStatus[id] = LOAD_STATUS_COMPLETE;
|
||||
gSeqLoadStatus[id] = LOAD_STATUS_COMPLETE;
|
||||
osSendMesg(retQueue, OS_MESG_32(retData << 0x18), OS_MESG_NOBLOCK);
|
||||
return Audio_LoadBlob(gAudioSeq, table->entries[id].romAddr);
|
||||
case FONT_TABLE:
|
||||
@ -719,7 +722,7 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData,
|
||||
case SAMPLE_TABLE:
|
||||
gSampleFontLoadStatus[id] = LOAD_STATUS_COMPLETE;
|
||||
// LTODO: Validate this
|
||||
// return Audio_LoadSample(table->entries[id].romAddr, 0);
|
||||
return Audio_LoadSample(table->entries[id].romAddr, table->entries[id], id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -736,39 +739,39 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData,
|
||||
romAddr = table->entries[id].romAddr;
|
||||
loadStatus = LOAD_STATUS_COMPLETE;
|
||||
|
||||
// switch (cachePolicy) {
|
||||
// case CACHEPOLICY_0:
|
||||
// ramAddr = AudioHeap_AllocPermanent(tableType, id, size);
|
||||
// if (ramAddr == NULL) {
|
||||
// return ramAddr;
|
||||
// }
|
||||
// loadStatus = LOAD_STATUS_PERMANENTLY_LOADED;
|
||||
// break;
|
||||
//
|
||||
// case CACHEPOLICY_1:
|
||||
// ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, id);
|
||||
// if (ramAddr == NULL) {
|
||||
// return ramAddr;
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case CACHEPOLICY_2:
|
||||
// ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, id);
|
||||
// if (ramAddr == NULL) {
|
||||
// return ramAddr;
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case CACHEPOLICY_3:
|
||||
// case CACHEPOLICY_4:
|
||||
// ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_EITHER, id);
|
||||
// if (ramAddr == NULL) {
|
||||
// return ramAddr;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// AudioLoad_StartAsyncLoad(romAddr, ramAddr, size, medium, nChunks, retQueue,
|
||||
// (retData << 0x18) | (tableType << 0x10) | (id << 8) | loadStatus);
|
||||
switch (cachePolicy) {
|
||||
case CACHEPOLICY_0:
|
||||
ramAddr = AudioHeap_AllocPermanent(tableType, id, size);
|
||||
if (ramAddr == NULL) {
|
||||
return ramAddr;
|
||||
}
|
||||
loadStatus = LOAD_STATUS_PERMANENTLY_LOADED;
|
||||
break;
|
||||
|
||||
case CACHEPOLICY_1:
|
||||
ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, id);
|
||||
if (ramAddr == NULL) {
|
||||
return ramAddr;
|
||||
}
|
||||
break;
|
||||
|
||||
case CACHEPOLICY_2:
|
||||
ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, id);
|
||||
if (ramAddr == NULL) {
|
||||
return ramAddr;
|
||||
}
|
||||
break;
|
||||
|
||||
case CACHEPOLICY_3:
|
||||
case CACHEPOLICY_4:
|
||||
ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_EITHER, id);
|
||||
if (ramAddr == NULL) {
|
||||
return ramAddr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
AudioLoad_StartAsyncLoad(romAddr, ramAddr, size, medium, nChunks, retQueue,
|
||||
(retData << 0x18) | (tableType << 0x10) | (id << 8) | loadStatus);
|
||||
}
|
||||
|
||||
switch (tableType) {
|
||||
|
@ -169,7 +169,8 @@ TunedSample* Audio_GetInstrumentTunedSample(Instrument* instrument, s32 arg1) {
|
||||
Instrument* Audio_GetInstrument(s32 fontId, s32 instId) {
|
||||
Instrument* instrument;
|
||||
|
||||
// LTODO: Remove this
|
||||
//fontId = 7;
|
||||
|
||||
if(gSoundFontList[fontId].instruments == NULL){
|
||||
gSoundFontList[fontId] = *Audio_LoadFont(gSoundFontTable->entries[fontId]);
|
||||
}
|
||||
|
@ -143,7 +143,9 @@ void AudioSeq_SeqLayerDisable(SequenceLayer* layer) {
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSeq_SeqLayerFree(SequenceChannel* channel, s32 layerIndex) {
|
||||
void AudioSeq_SeqLayerFree(SequenceChannel* channel, s32 layerIndex)
|
||||
{
|
||||
if (layerIndex < 4) {
|
||||
SequenceLayer* layer = channel->layers[layerIndex];
|
||||
|
||||
if (layer != NULL) {
|
||||
@ -152,6 +154,7 @@ void AudioSeq_SeqLayerFree(SequenceChannel* channel, s32 layerIndex) {
|
||||
channel->layers[layerIndex] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSeq_SequenceChannelDisable(SequenceChannel* channel) {
|
||||
s32 i;
|
||||
@ -907,7 +910,8 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
|
||||
sp52 = ((u16*) gSeqFontTable)[seqPlayer->seqId];
|
||||
loBits = gSeqFontTable[sp52];
|
||||
cmd = gSeqFontTable[sp52 + loBits - cmd];
|
||||
if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, cmd) != NULL) {
|
||||
//if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, cmd) != NULL)
|
||||
{
|
||||
channel->fontId = cmd;
|
||||
}
|
||||
/* fallthrough */
|
||||
@ -1013,7 +1017,11 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
|
||||
sp52 = ((u16*) gSeqFontTable)[seqPlayer->seqId];
|
||||
loBits = gSeqFontTable[sp52];
|
||||
cmd = gSeqFontTable[sp52 + loBits - cmd];
|
||||
if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, cmd) != NULL) {
|
||||
|
||||
printf("seqID: %02X\n", seqPlayer->seqId);
|
||||
|
||||
//if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, cmd) != NULL)
|
||||
{
|
||||
channel->fontId = cmd;
|
||||
}
|
||||
break;
|
||||
|
@ -667,6 +667,7 @@ Acmd* AudioSynth_Update(Acmd* aList, s32* cmdCount, s16* aiBufStart, s32 aiBufLe
|
||||
//if (gAudioBufferParams.ticksPerUpdate > 3 && gGameState != GSTATE_PLAY) return;
|
||||
aCmdPtr = aList;
|
||||
for (i = gAudioBufferParams.ticksPerUpdate; i > 0; i--) {
|
||||
|
||||
AudioSeq_ProcessSequences(i - 1);
|
||||
AudioSynth_SyncSampleStates(gAudioBufferParams.ticksPerUpdate - i);
|
||||
}
|
||||
@ -676,16 +677,16 @@ Acmd* AudioSynth_Update(Acmd* aList, s32* cmdCount, s16* aiBufStart, s32 aiBufLe
|
||||
// problem
|
||||
for (i = gAudioBufferParams.ticksPerUpdate; i > 0; i--) {
|
||||
if (i == 1) {
|
||||
printf("func_80009B64 i == 1\n");
|
||||
//printf("func_80009B64 i == 1\n");
|
||||
chunkLen = aiBufLen;
|
||||
} else if ((aiBufLen / i) >= gAudioBufferParams.samplesPerTickMax) {
|
||||
printf("func_80009B64 (aiBufLen / i) >= gAudioBufferParams.samplesPerTickMax\n");
|
||||
//printf("func_80009B64 (aiBufLen / i) >= gAudioBufferParams.samplesPerTickMax\n");
|
||||
chunkLen = gAudioBufferParams.samplesPerTickMax;
|
||||
} else if (gAudioBufferParams.samplesPerTickMin >= (aiBufLen / i)) {
|
||||
printf("func_80009B64 gAudioBufferParams.samplesPerTickMin >= (aiBufLen / i)\n");
|
||||
//printf("func_80009B64 gAudioBufferParams.samplesPerTickMin >= (aiBufLen / i)\n");
|
||||
chunkLen = gAudioBufferParams.samplesPerTickMin;
|
||||
} else {
|
||||
printf("func_80009B64 else\n");
|
||||
//printf("func_80009B64 else\n");
|
||||
chunkLen = gAudioBufferParams.samplesPerTick;
|
||||
}
|
||||
|
||||
@ -694,7 +695,7 @@ Acmd* AudioSynth_Update(Acmd* aList, s32* cmdCount, s16* aiBufStart, s32 aiBufLe
|
||||
AudioSynth_InitNextRingBuf(chunkLen, gAudioBufferParams.ticksPerUpdate - i, j);
|
||||
}
|
||||
}
|
||||
printf("chunkLen: %d, aiBufLen: %d \n", chunkLen, aiBufLen);
|
||||
//printf("chunkLen: %d, aiBufLen: %d \n", chunkLen, aiBufLen);
|
||||
|
||||
aCmdPtr =
|
||||
AudioSynth_DoOneAudioUpdate((s16*) aiBufPtr, chunkLen, aCmdPtr, gAudioBufferParams.ticksPerUpdate - i);
|
||||
@ -866,7 +867,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSub, NoteSynthesisSta
|
||||
s32 padE8;
|
||||
s32 padE4;
|
||||
s32 padE0;
|
||||
s32 skipBytes;
|
||||
s32 skipBytes = 0;
|
||||
s32 padD8;
|
||||
s32 padD4;
|
||||
s32 padD0;
|
||||
|
@ -34,8 +34,9 @@ void Load_RomFile(void* vRomAddress, void* dest, ptrdiff_t size) {
|
||||
|
||||
u8 Load_SceneFiles(Scene* scene) {
|
||||
#if 1
|
||||
bool hasSceneChanged = memcmp(&sCurrentScene, scene, sizeof(Scene)) != 0;
|
||||
sCurrentScene = *scene;
|
||||
return true;
|
||||
return hasSceneChanged;
|
||||
#else
|
||||
u8* ramPtr = SEGMENT_VRAM_START(ovl_i1);
|
||||
u8 segment;
|
||||
@ -167,13 +168,13 @@ u8 Load_SceneSetup(u8 sceneId, u8 sceneSetup) {
|
||||
case SCENE_VERSUS:
|
||||
changeScene = Load_SceneFiles(&sOvli2_Versus[sceneSetup]);
|
||||
if (changeScene == true) {
|
||||
// AUDIO_SET_SPEC_ALT(SFXCHAN_3, AUDIOSPEC_16);
|
||||
AUDIO_SET_SPEC_ALT(SFXCHAN_3, AUDIOSPEC_16);
|
||||
}
|
||||
break;
|
||||
case SCENE_LOGO:
|
||||
changeScene = Load_SceneFiles(&sNoOvl_Logo[sceneSetup]); // Logo does not load an overlay file
|
||||
if (changeScene == true) {
|
||||
// AUDIO_SET_SPEC(SFXCHAN_0, AUDIOSPEC_MA);
|
||||
AUDIO_SET_SPEC(SFXCHAN_0, AUDIOSPEC_MA);
|
||||
}
|
||||
break;
|
||||
case SCENE_CREDITS:
|
||||
|
@ -7,7 +7,7 @@ u16* Message_PtrFromId(u16 msgId) {
|
||||
|
||||
while (lookup->msgId != -1) {
|
||||
if (lookup->msgId == msgId) {
|
||||
return lookup->path;
|
||||
return ResourceGetDataByName(lookup->path);
|
||||
}
|
||||
lookup++;
|
||||
}
|
||||
|
@ -25,11 +25,14 @@
|
||||
#include <BlobFactory.h>
|
||||
#include <VertexFactory.h>
|
||||
#include "audio/GameAudio.h"
|
||||
//#include "sf64audio_provisional.h"
|
||||
|
||||
#include <Fast3D/gfx_pc.h>
|
||||
#include <Fast3D/gfx_rendering_api.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
//extern "C" AudioBufferParameters gAudioBufferParams;
|
||||
|
||||
#include <utility>
|
||||
|
||||
extern "C" {
|
||||
@ -151,7 +154,7 @@ void GameEngine::StartFrame() const {
|
||||
#define NUM_AUDIO_CHANNELS 2
|
||||
#define SAMPLES_PER_FRAME (SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3)
|
||||
|
||||
s16 audio_buffer[SAMPLES_PER_FRAME * 2] = { 0 };
|
||||
s16 audio_buffer[SAMPLES_PER_FRAME * 2 * 2] = { 0 };
|
||||
|
||||
extern "C" s32 audBuffer = 0;
|
||||
#include <sf64audio_provisional.h>
|
||||
@ -171,7 +174,11 @@ void GameEngine::HandleAudioThread() {
|
||||
}
|
||||
}
|
||||
|
||||
#define AUDIO_FRAMES_PER_UPDATE (gVIsPerFrame > 0 ? gVIsPerFrame : 1)
|
||||
//gVIsPerFrame = 2;
|
||||
|
||||
//#define AUDIO_FRAMES_PER_UPDATE (gVIsPerFrame > 0 ? gVIsPerFrame : 1)
|
||||
#define AUDIO_FRAMES_PER_UPDATE 2
|
||||
|
||||
|
||||
std::unique_lock<std::mutex> Lock(audio.mutex);
|
||||
int samples_left = AudioPlayerBuffered();
|
||||
@ -183,7 +190,7 @@ void GameEngine::HandleAudioThread() {
|
||||
countermin++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
|
||||
AudioThread_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS),
|
||||
num_audio_samples);
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ extern "C" SoundFont* Audio_LoadFont(AudioTableEntry entry) {
|
||||
}
|
||||
}
|
||||
|
||||
gSampleFontLoadStatus[font->sampleBankId1] = 2;
|
||||
gSampleFontLoadStatus[font->sampleBankId1] = LOAD_STATUS_COMPLETE;
|
||||
|
||||
gAudioCache[entry.romAddr] = font;
|
||||
return font;
|
||||
|
@ -277,7 +277,6 @@ void Graphics_ThreadUpdate() {
|
||||
// osRecvMesg(&gGfxVImsgQueue, NULL, OS_MESG_BLOCK);
|
||||
// }
|
||||
|
||||
// LTODO: There is no audio for now :P
|
||||
osSendMesg(&gTaskMesgQueue, OS_MESG_PTR(NULL), OS_MESG_NOBLOCK);
|
||||
Audio_Update();
|
||||
}
|
||||
@ -363,7 +362,6 @@ void Main_ThreadEntry(void* arg0) {
|
||||
OSMesg ogMsg;
|
||||
u32 mesg;
|
||||
|
||||
// LTODO: Implement audio
|
||||
Audio_ThreadEntry(NULL);
|
||||
Graphics_ThreadEntry(NULL);
|
||||
Controller_Init();
|
||||
|
Loading…
Reference in New Issue
Block a user