Сайт Влада

Как исправить баг с загрузкой Pattern Load Cues

Автор гида: FraGag, Перевод на русский язык и модификация: vladikcomper
Оригинальный гид: «SCHG How-to:Fix a race condition with Pattern Load Cues»

В Соник 1 и Соник 2 на первом этапе загрузки Pattern Load Cues, или сокращенно PLC, в редких случаях может произойти баг, приводящий к падению игры. Баг можно заметить в Соник 1 на уровнях LZ1 и LZ2: если вода находится на определенном уровне относительно начала экрана, игра зависает про попытке загрузить арт для экрана 'SONIC HAS PASSED' (см. видео)

В Соник 2 создать условия для возникновения бага почти невозможно. В Соник 3 и Соник и Наклз баг был исправлен как показано ниже.

Исправление бага

Шаги, рассказанные ниже, действуют как для кода Соник 1 (Hivebrain's 2005 disassembly / S1HS2 disassembly), так и для Соник 2 (Xenowhirl's Disassembly). Код, с которыми вы будете работать, в обоих играх абсолютно одинаков, будут только несколько визуальных отличий между дизасемблами. Представленный ниже код взять из Соник 1, дизасембл S1HS2.

Найдите процедуру "RunPLC_RAM", вы увидите следующий код:

; ---------------------------------------------------------------------------
; Subroutine to use graphics in PLC queue
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


RunPLC_RAM:				; XREF: Pal_FadeTo
		tst.l	($FFFFF680).w	; is PLC queue empty?
		beq.s	RunPLC_Rts	; if yes, branch
		tst.w	($FFFFF6F8).w	; is decompression in progress?
		bne.s	RunPLC_Rts	; if yes, branch
		movea.l	($FFFFF680).w,a0 ; load PLC queue
		lea	(loc_1502).l,a3
		lea	($FFFFAA00).w,a1
		move.w	(a0)+,d2
		bpl.s	loc_160E
		adda.w	#$A,a3

loc_160E:
		andi.w	#$7FFF,d2
		move.w	d2,($FFFFF6F8).w
		bsr.w	NemDec4
		move.b	(a0)+,d5
		asl.w	#8,d5
		move.b	(a0)+,d5
		moveq	#$10,d6
		moveq	#0,d0
		move.l	a0,($FFFFF680).w
		move.l	a3,($FFFFF6E0).w
		move.l	d0,($FFFFF6E4).w
		move.l	d0,($FFFFF6E8).w
		move.l	d0,($FFFFF6EC).w
		move.l	d5,($FFFFF6F0).w
		move.l	d6,($FFFFF6F4).w

RunPLC_Rts:
		rts
; End of function RunPLC_RAM

Переместите строчку

		move.w	d2,($FFFFF6F8).w

на новое место, прямо за этой строкой:

		move.l	d6,($FFFFF6F4).w

Причины бага

Pattern Load Cues - это механизм, разделяющий загрузку сжатых алгоритмом Nemesis тайлов на несколько кадров, чтобы игра могла синхронно (не зависая) работать наряду декомпрессором тайлов, так как этот процесс отнимает очень много времени. В начале PLC запроса, некоторые тайлы разжимаются на месте, затем процедура настраивает значения в памяти, чтобы другая часть декомпрессора смогла продолжить декомпрессию тайлов. Тайлы разжимаются небольшими порциями во время вертикального прерывания (VBlank), этот процесс растягивается на несколько кадров, и благодаря этому остальные части игры также могут функционировать. Баг происходит из-за того, что одно из значений ($FFF6F8, флаг PLC) задается до декомпрессии первых тайлов, в то время как остальные задаются после. И если вертикальные или горизонтальные прерывания (VBlank, HBlank) случаются во время начальной декомпрессии, процедуры в коде VBlank'а (sub_1642 и sub_165E) попытаются "подхватить" (продолжить) декомпрессию, потому что флаг $FFF6F8 активен, однако остальные значения в этот момент не инициализированы. В частности, адрес $FFF6E0, который ссылается одну из процедур декомпрессора, будет ссылаться на неверное место. Это и проводит к падению игры с ошибкой ILLEGAL INSTRUCTION.

Чтобы исправить проблему, флаг $FFF6F8 должен быть задан после того, как все значения инициализированы.