Preserve stage-1 stability fixes alongside v5 lighting, IPC v2, weapons and studio updates. Fix barrel AOE pointer corruption and extend modal gameplay gating. Verify 137 editor checks, 191 ASM checks, lighting bake and preview parity. Document stage-2 scope without implementing it.
379 lines
13 KiB
NASM
379 lines
13 KiB
NASM
include engine_api.inc
|
|
|
|
AuditGetAsyncKeyState PROTO :DWORD
|
|
AuditGetForegroundWindow PROTO
|
|
AuditPlaySoundA PROTO :DWORD, :DWORD, :DWORD
|
|
GetAsyncKeyState TEXTEQU <AuditGetAsyncKeyState>
|
|
GetForegroundWindow TEXTEQU <AuditGetForegroundWindow>
|
|
PlaySoundA TEXTEQU <AuditPlaySoundA>
|
|
CopyFileA PROTO :DWORD, :DWORD, :DWORD
|
|
|
|
include include/engine_defs.inc
|
|
include include/font8x8.inc
|
|
include include/engine_data.inc
|
|
include include/engine_math.inc
|
|
include include/engine_map.inc
|
|
include include/engine_textures.inc
|
|
include include/engine_input.inc
|
|
include include/engine_physics.inc
|
|
include include/engine_entity.inc
|
|
include include/engine_light.inc
|
|
include include/engine_raycaster.inc
|
|
include include/engine_render2d.inc
|
|
include include/engine_combat.inc
|
|
include include/engine_gameplay.inc
|
|
include include/engine_cutscene.inc
|
|
include startup_parser.inc
|
|
include project_paths.inc
|
|
|
|
.data
|
|
resultCount dd 0
|
|
results dd 256 dup(0)
|
|
resultPath db 'engine_results.bin',0
|
|
anglePath db 'angles.bin',0
|
|
framePath db 'frame.bgra',0
|
|
projectFramePath db 'project_frame.bgra',0
|
|
subPath db 'sublevel_02.dat',0
|
|
mainPath db 'map.dat',0
|
|
arenaPath db 'arena.dat',0
|
|
blackPath db 'black.dat',0
|
|
badPath db 'truncated.dat',0
|
|
missingPath db 'missing.dat',0
|
|
legacy1Path db 'legacy1.dat',0
|
|
legacy2Path db 'legacy2.dat',0
|
|
badNamePath db 'badname.dat',0
|
|
blockedPath db 'badspawn.dat',0
|
|
overflowPath db 'overflow.dat',0
|
|
orphanPath db 'orphan.dat',0
|
|
duplicatePath db 'duplicate.dat',0
|
|
validPack db 'assets/imported.bin',0
|
|
badPack db 'assets/badpack.bin',0
|
|
defaultPixels db 'assets/defaults.bin',0
|
|
defaultFile dd 0
|
|
defaultBytes dd 0
|
|
testKeys dw 256 dup(0)
|
|
testFocus dd 1
|
|
snapshot dd 6 dup(0)
|
|
|
|
CHECK MACRO value, expected
|
|
mov eax, value
|
|
cmp eax, expected
|
|
sete al
|
|
movzx eax, al
|
|
mov ecx, resultCount
|
|
mov [results+ecx*4], eax
|
|
inc resultCount
|
|
ENDM
|
|
|
|
.code
|
|
AuditGetAsyncKeyState PROC key:DWORD
|
|
mov ecx, key
|
|
movzx eax, WORD PTR [testKeys+ecx*2]
|
|
ret
|
|
AuditGetAsyncKeyState ENDP
|
|
AuditGetForegroundWindow PROC
|
|
mov eax, testFocus
|
|
ret
|
|
AuditGetForegroundWindow ENDP
|
|
|
|
Dump PROC uses ebx, path:DWORD, dataPtr:DWORD, dataSize:DWORD
|
|
LOCAL file:DWORD, written:DWORD
|
|
invoke CreateFileA, path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
|
|
mov file, eax
|
|
invoke WriteFile, file, dataPtr, dataSize, ADDR written, 0
|
|
invoke CloseHandle, file
|
|
ret
|
|
Dump ENDP
|
|
|
|
CaptureState PROC uses eax esi edi ecx
|
|
mov eax, playerX
|
|
mov snapshot, eax
|
|
mov eax, playerY
|
|
mov snapshot[4], eax
|
|
mov eax, spriteCount
|
|
mov snapshot[8], eax
|
|
mov eax, transitionCount
|
|
mov snapshot[12], eax
|
|
mov eax, DWORD PTR [gameMap]
|
|
mov snapshot[16], eax
|
|
mov eax, mapCeilingColor
|
|
mov snapshot[20], eax
|
|
ret
|
|
CaptureState ENDP
|
|
|
|
StateUnchanged PROC uses ebx
|
|
mov eax, playerX
|
|
cmp eax, snapshot
|
|
jne state_changed
|
|
mov eax, playerY
|
|
cmp eax, snapshot[4]
|
|
jne state_changed
|
|
mov eax, spriteCount
|
|
cmp eax, snapshot[8]
|
|
jne state_changed
|
|
mov eax, transitionCount
|
|
cmp eax, snapshot[12]
|
|
jne state_changed
|
|
mov eax, DWORD PTR [gameMap]
|
|
cmp eax, snapshot[16]
|
|
jne state_changed
|
|
mov eax, mapCeilingColor
|
|
cmp eax, snapshot[20]
|
|
jne state_changed
|
|
mov eax, 1
|
|
ret
|
|
state_changed:
|
|
xor eax, eax
|
|
ret
|
|
StateUnchanged ENDP
|
|
|
|
include tests/cutscene_engine_checks.inc
|
|
include tests/lighting_engine_checks.inc
|
|
include tests/stability_engine_checks.inc
|
|
include tests/integration_engine_checks.inc
|
|
|
|
AuditMain PROC
|
|
call InitMathTables
|
|
invoke Dump, OFFSET anglePath, OFFSET tableRayAngleOffset, SCREEN_W*4
|
|
call ParseStartupMap
|
|
CHECK DWORD PTR [startupMapPath], 65636170h ; quoted command-line start map (pace...)
|
|
call GenerateDefaultTextures
|
|
invoke CreateFileA, OFFSET defaultPixels, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
|
|
mov defaultFile, eax
|
|
invoke ReadFile, defaultFile, OFFSET texturePackStaging, MAX_TEXTURES*TEX_SIZE_BYTES, OFFSET defaultBytes, 0
|
|
invoke CloseHandle, defaultFile
|
|
mov esi, OFFSET texturePackStaging
|
|
mov edi, OFFSET textureBuffer
|
|
mov ecx, MAX_TEXTURES*TEX_SIZE_BYTES/4
|
|
repe cmpsd
|
|
sete al
|
|
movzx eax, al
|
|
CHECK eax, 1 ; editor fallback previews match all engine default pixels
|
|
CHECK DWORD PTR [textureBuffer+10*TEX_SIZE_BYTES], COLOR_CHROMA_KEY ; default lamp exists independently of portal
|
|
invoke CopyFileA, OFFSET validPack, OFFSET szTexPackPath, 0
|
|
mov ecx, 128
|
|
dirty_stack:
|
|
push 0A5A5A5A5h
|
|
loop dirty_stack
|
|
add esp, 512
|
|
call LoadTexturePack
|
|
CHECK eax, 1 ; valid texture pack loads with dirty stack
|
|
CHECK DWORD PTR [textureBuffer+TEX_SIZE_BYTES], 00112233h ; imported PNG reaches ASM unchanged
|
|
CHECK DWORD PTR [textureBuffer+11*TEX_SIZE_BYTES], COLOR_CHROMA_KEY ; imported sprite transparency reaches ASM
|
|
invoke CopyFileA, OFFSET badPack, OFFSET szTexPackPath, 0
|
|
call LoadTexturePack
|
|
CHECK eax, 0 ; truncated texture pack rejected
|
|
CHECK DWORD PTR [textureBuffer+TEX_SIZE_BYTES], 00112233h ; failed pack load preserves current pixels
|
|
|
|
mov startupMapPath, 0
|
|
call LoadMapBinary
|
|
CHECK eax, 1 ; startup main map loads
|
|
CHECK spriteCount, 2 ; startup sprite count
|
|
CHECK transitionCount, 1 ; startup transition count
|
|
invoke SwitchSublevel, OFFSET subPath, 4, 6, 0, 0
|
|
CHECK spriteCount, 1 ; instant transition replaces sprites
|
|
CHECK playerX, 288 ; destination X spawn
|
|
CHECK playerY, 416 ; destination Y spawn
|
|
invoke SwitchSublevel, OFFSET mainPath, 2, 2, 0, 1
|
|
mov esi, 32
|
|
fade_frames:
|
|
call ApplyScreenFade
|
|
dec esi
|
|
jnz fade_frames
|
|
CHECK fadeState, 0 ; fade finishes and releases player
|
|
CHECK fadeLevel, 0 ; fade returns full brightness
|
|
CHECK spriteCount, 2 ; fade transition loads destination
|
|
invoke LoadSublevelBinary, OFFSET arenaPath, -1, -1, 0
|
|
CHECK eax, 1 ; map basename beginning with a resolves in assets
|
|
invoke LoadSublevelBinary, OFFSET legacy1Path, -1, -1, 0
|
|
CHECK eax, 1 ; legacy v1 accepted
|
|
CHECK DWORD PTR [spriteList], 224 ; legacy sprite coordinates converted
|
|
invoke LoadSublevelBinary, OFFSET legacy2Path, -1, -1, 0
|
|
CHECK eax, 1 ; legacy extended v2 accepted
|
|
CHECK transitionCount, 1 ; legacy v2 transitions retained
|
|
invoke LoadSublevelBinary, OFFSET blackPath, -1, -1, 0
|
|
CHECK eax, 1 ; black palette map loads
|
|
CHECK mapCeilingColor, 0 ; black ceiling applied
|
|
CHECK mapFloorColor, 0 ; black floor applied
|
|
|
|
call CaptureState
|
|
invoke SwitchSublevel, OFFSET missingPath, 2, 2, 0, 0
|
|
call StateUnchanged
|
|
CHECK eax, 1 ; missing destination preserves current room
|
|
CHECK mapErrorFrames, 180 ; failed transition reports an error
|
|
invoke LoadSublevelBinary, OFFSET badPath, -1, -1, 0
|
|
CHECK eax, 0 ; truncated map rejected
|
|
call StateUnchanged
|
|
CHECK eax, 1 ; truncated load preserves current room
|
|
invoke LoadSublevelBinary, OFFSET badNamePath, -1, -1, 0
|
|
CHECK eax, 0 ; unterminated target filename rejected
|
|
invoke LoadSublevelBinary, OFFSET blockedPath, -1, -1, 0
|
|
CHECK eax, 0 ; blocked spawn rejected
|
|
invoke LoadSublevelBinary, OFFSET overflowPath, -1, -1, 0
|
|
CHECK eax, 0 ; object count overflow rejected without clamping
|
|
invoke LoadSublevelBinary, OFFSET orphanPath, -1, -1, 0
|
|
CHECK eax, 0 ; portal without transition rejected
|
|
invoke LoadSublevelBinary, OFFSET duplicatePath, -1, -1, 0
|
|
CHECK eax, 0 ; duplicate triggers rejected
|
|
invoke LoadSublevelBinary, OFFSET mainPath, 0, 0, 0
|
|
CHECK eax, 0 ; transition spawn inside a wall rejected
|
|
call StateUnchanged
|
|
CHECK eax, 1 ; invalid spawn leaves current room intact
|
|
|
|
mov esi, OFFSET missingPath
|
|
mov edi, OFFSET startupMapPath
|
|
copy_missing_startup:
|
|
lodsb
|
|
stosb
|
|
test al, al
|
|
jnz copy_missing_startup
|
|
call LoadMapBinary
|
|
CHECK DWORD PTR [gameMap], 01010101h ; missing startup map creates the procedural fallback
|
|
CHECK spriteCount, 0 ; fallback clears stale sprites
|
|
CHECK transitionCount, 0 ; fallback clears stale transitions
|
|
mov startupMapPath, 0
|
|
|
|
; Re-entering an occupied destination trigger must not bounce back.
|
|
invoke LoadSublevelBinary, OFFSET subPath, 2, 2, 0
|
|
mov mapErrorFrames, 0
|
|
call CheckPortalTriggers
|
|
CHECK spriteCount, 1 ; destination trigger is latched on spawn
|
|
mov playerX, 224
|
|
call CheckPortalTriggers
|
|
mov playerX, 160
|
|
call CheckPortalTriggers
|
|
CHECK fadeState, 1 ; leaving and re-entering a trigger activates it
|
|
mov fadeState, 0
|
|
mov fadeLevel, 0
|
|
|
|
; Input APIs are mocked; PollInput itself is the production procedure.
|
|
mov hEngineWnd, 1
|
|
mov testKeys[VK_KEY_SHIFT*2], 8000h
|
|
call PollInput
|
|
movzx eax, keyRun
|
|
CHECK eax, 1 ; Shift starts sprint
|
|
mov testKeys[VK_KEY_SHIFT*2], 0
|
|
call PollInput
|
|
movzx eax, keyRun
|
|
CHECK eax, 0 ; releasing Shift stops sprint
|
|
mov testKeys[VK_KEY_E*2], 8000h
|
|
call PollInput
|
|
movzx eax, keyAction
|
|
CHECK eax, 1 ; action emitted on first key press
|
|
call PollInput
|
|
movzx eax, keyAction
|
|
CHECK eax, 0 ; held action does not repeat
|
|
mov testKeys[VK_KEY_E*2], 0
|
|
call PollInput
|
|
mov testKeys[VK_KEY_E*2], 8000h
|
|
call PollInput
|
|
movzx eax, keyAction
|
|
CHECK eax, 1 ; releasing action re-arms it
|
|
mov testFocus, 0
|
|
call PollInput
|
|
movzx eax, keyAction
|
|
CHECK eax, 0 ; unfocused engine ignores keyboard
|
|
|
|
; Flat wall at x=640, player x=160: every perpendicular depth is 480.
|
|
mov edi, OFFSET gameMap
|
|
mov ecx, MAP_SIZE
|
|
xor eax, eax
|
|
rep stosb
|
|
xor ecx, ecx
|
|
straight_wall:
|
|
mov eax, ecx
|
|
shl eax, 5
|
|
mov byte ptr [gameMap+eax+10], CELL_BRICK
|
|
inc ecx
|
|
cmp ecx, MAP_H
|
|
jl straight_wall
|
|
mov playerX, 160
|
|
mov playerY, 1024
|
|
mov playerAngle, 0
|
|
mov spriteCount, 0
|
|
call Render3DScene
|
|
mov esi, OFFSET depthBuffer
|
|
mov ecx, SCREEN_W
|
|
mov ebx, 1
|
|
check_flat_depth:
|
|
cmp DWORD PTR [esi], 480
|
|
je flat_depth_ok
|
|
xor ebx, ebx
|
|
flat_depth_ok:
|
|
add esi, 4
|
|
loop check_flat_depth
|
|
CHECK ebx, 1 ; all 320 columns of a flat wall have exactly the same depth
|
|
invoke Dump, OFFSET framePath, OFFSET screenBuffer, FRAME_BUFFER_SIZE
|
|
|
|
; Overlapping sprites are sorted far-to-near, independent of insertion order.
|
|
mov spriteCount, 2
|
|
mov esi, OFFSET spriteList
|
|
mov [esi].SpriteStruct.x, 288
|
|
mov [esi].SpriteStruct.y, 1024
|
|
mov [esi].SpriteStruct.scale, FIXED_ONE
|
|
mov [esi].SpriteStruct.textureId, 11
|
|
add esi, sizeof SpriteStruct
|
|
mov [esi].SpriteStruct.x, 416
|
|
mov [esi].SpriteStruct.y, 1024
|
|
mov [esi].SpriteStruct.scale, FIXED_ONE
|
|
mov [esi].SpriteStruct.textureId, 12
|
|
call Render3DScene
|
|
CHECK spriteOrder, 1 ; farther sprite draws first
|
|
CHECK spriteOrder[4], 0 ; nearer sprite draws last
|
|
|
|
; Sprite slot 10 can be replaced without repainting wall portals.
|
|
mov edi, OFFSET textureBuffer+10*TEX_SIZE_BYTES
|
|
mov ecx, TEX_SIZE_BYTES/4
|
|
mov eax, COLOR_RED
|
|
rep stosd
|
|
mov edi, OFFSET portalTextureBuffer
|
|
mov ecx, TEX_SIZE_BYTES/4
|
|
mov eax, COLOR_GREEN
|
|
rep stosd
|
|
mov byte ptr [gameMap+16*MAP_W+10], CELL_PORTAL
|
|
mov spriteCount, 0
|
|
call Render3DScene
|
|
CHECK DWORD PTR [screenBuffer+120*SCREEN_PITCH+160*4], 00007F00h ; portal uses separate artwork despite imported sprite slot 10
|
|
mov byte ptr [gameMap+16*MAP_W+10], CELL_BRICK
|
|
|
|
; Scaling a sprite changes its height, not the floor point at its depth.
|
|
mov edi, OFFSET textureBuffer+11*TEX_SIZE_BYTES
|
|
mov ecx, TEX_SIZE_BYTES/4
|
|
mov eax, COLOR_RED
|
|
rep stosd
|
|
mov spriteCount, 1
|
|
mov esi, OFFSET spriteList
|
|
mov [esi].SpriteStruct.scale, 2*FIXED_ONE
|
|
call Render3DScene
|
|
CHECK DWORD PTR [screenBuffer+179*SCREEN_PITCH+160*4], COLOR_RED ; scaled sprite still reaches its floor point
|
|
mov eax, DWORD PTR [screenBuffer+180*SCREEN_PITCH+160*4]
|
|
cmp eax, COLOR_RED
|
|
setne al
|
|
movzx eax, al
|
|
CHECK eax, 1 ; scaled sprite does not sink into the floor
|
|
|
|
; Cardinal rays must not divide by zero or wrap DDA distances.
|
|
mov spriteCount, 0
|
|
mov playerAngle, ANGLE_90
|
|
call Render3DScene
|
|
CHECK DWORD PTR [depthBuffer+160*4], 1024 ; exact cardinal ray handles zero X direction
|
|
|
|
call GenerateDefaultTextures
|
|
invoke CopyFileA, OFFSET projectPackPath, OFFSET szTexPackPath, 0
|
|
call LoadTexturePack
|
|
CHECK eax, 1 ; actual project texture pack loads
|
|
invoke LoadSublevelBinary, OFFSET projectMapPath, -1, -1, 0
|
|
CHECK eax, 1 ; actual project map loads in ASM
|
|
call Render3DScene
|
|
invoke Dump, OFFSET projectFramePath, OFFSET screenBuffer, FRAME_BUFFER_SIZE
|
|
|
|
call RunLightingChecks
|
|
call RunCutsceneChecks
|
|
call RunStabilityChecks
|
|
mov eax, resultCount
|
|
inc eax
|
|
shl eax, 2
|
|
invoke Dump, OFFSET resultPath, OFFSET resultCount, eax
|
|
invoke ExitProcess, 0
|
|
AuditMain ENDP
|
|
END AuditMain
|