Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jak 2] Fix possible stereo desync in overlord #2663

Merged
merged 4 commits into from
May 20, 2023

Conversation

water111
Copy link
Collaborator

Normally, when they allocate a VagCmd, they do a bunch of stuff to clear all the status bits and reset things
in particular the InitVAGCmd function does a lot

image

but for the stereo command, they do a lot less:
image

Which means that the new_stereo_command can just have random status bits left over from whatever the last user had.
we seem to end up in a state where byte21 is set, and this causes everything else to be wrong and off-by-one dma transfer. My guess is that the original game avoided this bug due to lucky timing that I don't understand.

I think the fix of just clearing byte21 is ok because there's no way that the old value of the byte is useful after the command is repurposed.

game/overlord/jak2/vag.h Outdated Show resolved Hide resolved
@water111 water111 merged commit d5951c2 into master May 20, 2023
13 checks passed
Comment on lines 965 to +971
u32 GetSpuRamAddress(VagCmd* param_1) {
bool bVar1;
u32 uVar2;
u32 uVar3;
u32 uVar4;
u32 uVar5;
u32 uVar6;
u32 uVar7;

uVar7 = param_1->spu_stream_dma_mem_addr;
uVar6 = (param_1->voice & 0xffffU) | 0x2240;
do {
uVar5 = 0;
do {
uVar2 = sceSdGetAddr(uVar6);
uVar3 = sceSdGetAddr(uVar6);
uVar4 = sceSdGetAddr(uVar6);
// printf("got nax: %d\n", uVar3);
if ((uVar2 == uVar3) ||
((uVar3 != uVar4 && (bVar1 = uVar2 == uVar4, uVar4 = uVar5, bVar1)))) {
uVar4 = uVar2;
}
uVar5 = uVar4;
} while (uVar4 == 0);
} while ((uVar4 < uVar7) || (uVar7 + 0x4040 <= uVar4));
return uVar4;
// this is simplified a lot from the original.
// they seem to sanity check if the value is reasonable or not, but the sanity check can fail
// if the overlord thread isn't keeping up.
// as far as I can tell, it's totally fine to discard these checks because our sceSdGetAddr
// works perfectly.
return sceSdGetAddr((param_1->voice & 0xffffU) | 0x2240);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's two things going on here. There's the safety check of calling sceSdGetAddr three times, this is because the SPU address registers are composed of two 16bit registers which means there's risk of tearing when reading. This part can safely be omitted for us.

The other check seems to be that the address is contained within the allocated region (0x4040 bytes), if this is actually happening it means the voice has escaped the allocated area, this might be kinda bad if it actually happens for us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants