diff --git a/base-hack/asm/functions/media.asm b/base-hack/asm/functions/media.asm index 6a4ad79b0..98c026fe7 100644 --- a/base-hack/asm/functions/media.asm +++ b/base-hack/asm/functions/media.asm @@ -19,4 +19,6 @@ .definelabel RainbowCoinFTT, 0x806F5498 .definelabel wipeTurnedInArray, 0x806C7B00 .definelabel alCSPSetTempo, 0x80738320 -.definelabel getSongTempo, 0x807383B0 \ No newline at end of file +.definelabel getSongTempo, 0x807383B0 +.definelabel getNextFreeSynthUpdate, 0x80739C88 +.definelabel SetParam, 0x8073F1E4 \ No newline at end of file diff --git a/base-hack/asm/hookcode/io.asm b/base-hack/asm/hookcode/io.asm index 789d01b35..adb4a5eef 100644 --- a/base-hack/asm/hookcode/io.asm +++ b/base-hack/asm/hookcode/io.asm @@ -218,6 +218,30 @@ dynflagcheck_3: j 0x80631E44 nop +stopVoiceFail: + lw $t8, 0x8 ($a0) + addiu $v0, $zero, 0x1 + j 0x8073B740 + sb $v0, 0x88 ($t8) ;byte 1 offset + +stopVoiceFail2: + bne $t9, $zero, stopVoiceNotFail + nop + lw $t8, 0x24 ($sp) + addiu $v0, $zero, 0x1 + j 0x8073CC48 + sb $v0, 0x88 ($t8) ;byte 1 offset + + stopVoiceNotFail: + j 0x8073CBF8 + nop + +freeVoiceFail: + lw $t2, 0x8 ($a0) + addiu $t4, $zero, 0x2 + j 0x8073B814 + sb $t4, 0x89 ($t2) ;byte 2 offset + pleaseDontStopIslesMusic: addiu $at, $at, 0xA0A8 ; currentMap pointer lw $a0, 0x0 ($at) diff --git a/base-hack/asm/symbols.asm b/base-hack/asm/symbols.asm index 34164bae5..aec770cfd 100644 --- a/base-hack/asm/symbols.asm +++ b/base-hack/asm/symbols.asm @@ -209,6 +209,7 @@ .definelabel MusicTrackChannels, 0x807458DC .definelabel BoatSpeeds, 0x8075A04C .definelabel textParameter, 0x80750AC8 +.definelabel synthesizer, 0x8076D708 .definelabel collisionType, 0x807FBD70 .definelabel collisionActive, 0x807FBB85 diff --git a/base-hack/include/common_structs.h b/base-hack/include/common_structs.h index 081166db8..96c022e5d 100644 --- a/base-hack/include/common_structs.h +++ b/base-hack/include/common_structs.h @@ -2396,4 +2396,27 @@ typedef struct warp_info_data { typedef struct coinHUDStruct { /* 0x000 */ unsigned short map_id; /* 0x004 */ unsigned short requirement; -} coinHUDStruct; \ No newline at end of file +} coinHUDStruct; + +typedef struct ALParam_s { + /* 0x000 */ struct ALParam_s* next; + /* 0x004 */ int delta; + /* 0x008 */ short updateType; + /* 0x00A */ short unity; + /* 0x00C */ int pitch; + /* 0x010 */ short volume; + /* 0x012 */ char pan; + /* 0x013 */ char fxmix; + /* 0x014 */ char unk_14[0x1C-0x14]; + /* 0x01C */ int samples; + /* 0x020 */ struct ALWaveTable_s* wave; +} ALParam; + +// This is ugly, but gets very bloated and hard to read otherwise +typedef struct PVoice { + /* 0x000 */ struct ALLink_s node; + /* 0x008 */ char unk_04[0x88-0x08]; + /* 0x088 */ char debug1; + /* 0x089 */ char debug2; + /* 0x08A */ short delay; +} PVoice; \ No newline at end of file diff --git a/base-hack/include/dk64.h b/base-hack/include/dk64.h index ba341a8bc..1515251e8 100644 --- a/base-hack/include/dk64.h +++ b/base-hack/include/dk64.h @@ -513,6 +513,9 @@ extern void customDamageCode(void); extern void unkSoundFunction(void *actor, int unk); extern int isModelTwoTiedFlagSet(maps map, int id); +extern ALParam* getNextFreeSynthUpdate(void); +extern void SetParam(PVoice* pvoice, short type, ALParam* param); + //vanilla data extern OSThread* __osActiveQueue; extern float TransitionSpeed; @@ -718,6 +721,7 @@ extern unsigned char SongInWriteSlot[4]; extern short songVolumes[SONG_COUNT]; extern ALCSPlayer* compactSequencePlayers[4]; extern ALCMidiHdr* musicStorage[4]; +extern ALSynth synthesizer; extern unsigned int DKTVData[5]; extern void* ExitPointer; diff --git a/base-hack/include/global.h b/base-hack/include/global.h index 6641b3c0c..a7154de71 100644 --- a/base-hack/include/global.h +++ b/base-hack/include/global.h @@ -409,6 +409,7 @@ extern void handleCrownTimerInternal(void); extern void initSongDisplay(int song); extern Gfx* displaySongNameHandler(Gfx* dl); extern void resetDisplayedMusic(void); +extern char postSynUpdate(PVoice* pVoice, int delta, short type); extern enum_bonus_skin getBarrelSkinIndex(int actor); extern enum_bonus_skin getShopSkinIndex(item_packet *data); diff --git a/base-hack/src/misc/music_text.c b/base-hack/src/misc/music_text.c index 2b090362c..e073c4b37 100644 --- a/base-hack/src/misc/music_text.c +++ b/base-hack/src/misc/music_text.c @@ -162,4 +162,52 @@ Gfx* displaySongNameHandler(Gfx* dl) { gSPPopMatrix(dl++, G_MTX_MODELVIEW); } return dl; -} \ No newline at end of file +} + +void fixBrokenVoices(ALSeqPlayer* seq_p) { + if(SongInWriteSlot[2] != 0x2B || MusicTrackChannels[8] == 0x12){ + seq_p->state = 1; + return; + } + PVoice* pVoice = (PVoice*) 0x8076D714; + int* samples = *(int*) 0x8076D724; + int delta = * samples; + while(pVoice != 0){ + // if stop voice update didn't come through + if (pVoice->debug1 == 0x01){ + if(postSynUpdate(pVoice, (pVoice->delay + samples), 0xF) == 1){ + pVoice->debug1 = 0; + } + } + // if stop voice update came through, but free voice update didn't come through + if (pVoice->debug1 == 0 && pVoice->debug2 == 0x02){ + if(postSynUpdate(pVoice, (pVoice->delay + samples), 0) == 1){ + pVoice->debug1 = 0; + pVoice->debug2 = 0; + } + } + pVoice = (PVoice*) (int*) pVoice->node.next; + } + + // finally set the sequence player's state to 1 (playing), which is the code that this function overwrites + seq_p->state = 1; +} + +char postSynUpdate(PVoice* pVoice, int delta, short type){ + // Sends an update to the Synthesizer. type 0xF is for stopping voices and type 0x0 is for freeing voices + if(pVoice){ + // Typical dk64 bureaucracy, everything goes through updates, very much similar to MIDI's events + // but yeah, we need one, so let's see if we can get one + ALParam* param = getNextFreeSynthUpdate(); + if(param != 0){ + param->delta = delta; + param->updateType = type; + param->pitch = (int*) pVoice; + // the "3" here signifies that we're adding an update to a pVoice. + // the exact function in dk64 is... a function of all time, + SetParam(pVoice, 3, param); + return 1; + } + } + return 0; +} \ No newline at end of file diff --git a/randomizer/Patching/ASMPatcher.py b/randomizer/Patching/ASMPatcher.py index e0e1f2c0b..ba443b455 100644 --- a/randomizer/Patching/ASMPatcher.py +++ b/randomizer/Patching/ASMPatcher.py @@ -1689,10 +1689,60 @@ def patchAssembly(ROM_COPY, spoiler): writeFunction(ROM_COPY, 0x8061DD80, Overlay.Static, "pressSkipHandler", offset_dict) # Handler for press start to skip # Music Fix + # Increase music resources to mitigate the music bug killing the audio engine writeValue(ROM_COPY, 0x807452B0, Overlay.Static, 0xD00, offset_dict, 4) writeValue(ROM_COPY, 0x80600DA2, Overlay.Static, 0x38, offset_dict) writeValue(ROM_COPY, 0x80600DA6, Overlay.Static, 0x70, offset_dict) + # Repair the audio engine if damage did occur + # Write debug bytes to identify permanently damaged voices and what kind of damage they suffer from + writeHook(ROM_COPY, 0x8073B6E4, Overlay.Static, "stopVoiceFail", offset_dict) + writeHook(ROM_COPY, 0x8073B798, Overlay.Static, "freeVoiceFail", offset_dict) + # This particular hook is pretty ugly, but I don't see another way. It's probably + # explicitly not a function because of how deep this might be in the stack with handleMIDIEvents + writeHook(ROM_COPY, 0x8073CBF0, Overlay.Static, "stopVoiceFail2", offset_dict) + + # Write function that fixes the permanently damaged voices + writeFunction(ROM_COPY, 0x80733750, Overlay.Static, "fixBrokenVoices", offset_dict) + writeValue(ROM_COPY, 0x80733754, Overlay.Static, 0x8FA40074, offset_dict, 4) # LW a0, 0x74 (sp) + + # Accomodate for there being 2 new debug byte fields in a struct that didn't have them + # These are pairs of writes that change each command from LW to LH and increase the offset to + # compensate for the changed data type. This only affects read commands, because + # whenever the delay is set, the voice can't possibly be in a bad state in terms of this change. + writeValue(ROM_COPY, 0x8073B704, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073B707, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073B7B8, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073B7BB, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073B88C, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073B88F, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073B958, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073B95B, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073BA08, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073BA0B, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073BAB8, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073BABB, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073C878, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073C87B, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073CE30, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073CE33, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073CF58, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073CF5B, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073D008, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073D00B, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + + writeValue(ROM_COPY, 0x8073CD68, Overlay.Static, 0x85, offset_dict, 1) # LW -> LH + writeValue(ROM_COPY, 0x8073CD6B, Overlay.Static, 0x8A, offset_dict, 1) # 0x88 -> 0x8A + # Soundplayer Fix writeValue(ROM_COPY, 0x80735C9E, Overlay.Static, 0xFFFF, offset_dict) # initSoundPlayer creates the event writeValue(ROM_COPY, 0x80735D0E, Overlay.Static, 0xFFFF, offset_dict) # __sndpVoiceHandler checks for the event