From 66888d2c8e34434ec7c97d6f749e735e17f93c5c Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 14 Jun 2026 14:36:07 +0200 Subject: [PATCH] fix(textures): DecodeSolidColor null-safe against null ColorValue A Base1Solid (or OrigTextureId==0) Surface can carry a null ColorValue; DecodeSolidColor dereferenced it (color.Alpha) and threw NullReferenceException. It is called directly from TextureCache.DecodeFromDats, OUTSIDE DecodeRenderSurface's try/catch, so the NRE crashed the whole client. Surfaced by the D.2b chrome prove-out feeding UI surface ids. Guard null -> Magenta (the decoder's existing "undecodable" sentinel). Test added. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/AcDream.Core/Textures/SurfaceDecoder.cs | 5 +++++ .../Textures/SurfaceDecoderSolidColorTests.cs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/AcDream.Core.Tests/Textures/SurfaceDecoderSolidColorTests.cs diff --git a/src/AcDream.Core/Textures/SurfaceDecoder.cs b/src/AcDream.Core/Textures/SurfaceDecoder.cs index 49cfe199..f727a59c 100644 --- a/src/AcDream.Core/Textures/SurfaceDecoder.cs +++ b/src/AcDream.Core/Textures/SurfaceDecoder.cs @@ -80,6 +80,11 @@ public static class SurfaceDecoder /// public static DecodedTexture DecodeSolidColor(DatReaderWriter.Types.ColorARGB color, float translucency) { + // Malformed Base1Solid (or OrigTextureId==0) surface with no color value: + // signal undecodable (Magenta) instead of NRE. This method is called + // directly from TextureCache.DecodeFromDats, OUTSIDE DecodeRenderSurface's + // try/catch, so it must be null-safe itself. + if (color is null) return DecodedTexture.Magenta; float opacity = Math.Clamp(1f - translucency, 0f, 1f); byte alpha = (byte)Math.Clamp(color.Alpha * opacity, 0f, 255f); return new DecodedTexture( diff --git a/tests/AcDream.Core.Tests/Textures/SurfaceDecoderSolidColorTests.cs b/tests/AcDream.Core.Tests/Textures/SurfaceDecoderSolidColorTests.cs new file mode 100644 index 00000000..ffb4b427 --- /dev/null +++ b/tests/AcDream.Core.Tests/Textures/SurfaceDecoderSolidColorTests.cs @@ -0,0 +1,17 @@ +using AcDream.Core.Textures; +using Xunit; + +namespace AcDream.Core.Tests.Textures; + +public class SurfaceDecoderSolidColorTests +{ + [Fact] + public void DecodeSolidColor_NullColor_ReturnsMagenta_DoesNotThrow() + { + // A malformed Base1Solid surface can carry a null ColorValue. DecodeSolidColor + // is called outside DecodeRenderSurface's try/catch (from TextureCache), so it + // must be null-safe itself — return the undecodable sentinel, never NRE. + var result = SurfaceDecoder.DecodeSolidColor(null!, 0f); + Assert.Equal(DecodedTexture.Magenta, result); + } +}