using System.IO; using System.Collections.Generic; using System.Linq; namespace codelanguage; internal static class RuntimeIconResourceLoader { public static bool TryLoadAll(string path, string? stateName, int requestedDir, int requestedFrame, int? moving, out RuntimeIconData data, out int width, out int height) { data = new RuntimeIconData(); width = height = 0; if (!File.Exists(path)) return false; byte[] bytes; try { bytes = File.ReadAllBytes(path); } catch { return false; } if (!RuntimeRasterDecoder.TryDecode(bytes, out int sheetWidth, out int sheetHeight, out var sheet, out _)) return false; var metadata = new CraftIconFile(path); metadata.Load(); var states = metadata.States.Where(s => (stateName == null || s.Name == stateName) && (moving == null || s.Movement == (moving.Value != 0))).ToList(); if (stateName != null && states.Count == 0) return false; if (states.Count == 0) { if (bytes.Length >= 6 && (bytes.AsSpan(0, 6).SequenceEqual("GIF87a"u8) || bytes.AsSpan(0, 6).SequenceEqual("GIF89a"u8))) { if (!RuntimeRasterDecoder.TryDecodeGifFrames(bytes, out int gifWidth, out int gifHeight, out var gifFrames, out _)) return false; if (requestedFrame > gifFrames.Count) return false; var selectedFrames = requestedFrame > 0 ? new List { gifFrames[requestedFrame - 1] } : gifFrames; data.States.Add(new RuntimeIconState { Name = "", Width = gifWidth, Height = gifHeight, DirectionCount = 1, FrameCount = selectedFrames.Count, Frames = selectedFrames }); sheetWidth = gifWidth; sheetHeight = gifHeight; } else data.States.Add(new RuntimeIconState { Name = "", Width = sheetWidth, Height = sheetHeight, DirectionCount = 1, FrameCount = 1, Frames = new List { sheet } }); } else { int stateTop = 0; foreach (var state in metadata.States) { if (states.Contains(state)) { var dirs = requestedDir == 0 ? Enumerable.Range(0, state.DirCount).ToList() : new[] { DirectionIndex(requestedDir, state.DirCount) }.Where(i => i >= 0).ToList(); var frames = requestedFrame <= 0 ? Enumerable.Range(0, state.FrameCount).ToList() : new[] { requestedFrame - 1 }.Where(i => i >= 0 && i < state.FrameCount).ToList(); if (dirs.Count == 0 || frames.Count == 0) return false; var selectedFrames = new List(); foreach (int dir in dirs) foreach (int frame in frames) { int left = frame * state.Width, top = stateTop + dir * state.Height; if (left + state.Width > sheetWidth || top + state.Height > sheetHeight) return false; var pixels = new byte[checked(state.Width * state.Height * 4)]; for (int row = 0; row < state.Height; row++) Buffer.BlockCopy(sheet, ((top + row) * sheetWidth + left) * 4, pixels, row * state.Width * 4, state.Width * 4); selectedFrames.Add(pixels); } data.States.Add(new RuntimeIconState { Name = state.Name, Width = state.Width, Height = state.Height, Movement = state.Movement, DirectionCount = dirs.Count, FrameCount = frames.Count, Frames = selectedFrames }); } stateTop += state.Height * state.DirCount; } } if (data.States.Count == 0) return false; width = data.States[0].Width; height = data.States[0].Height; return true; } public static bool TryLoad(string path, string? stateName, int requestedDir, int requestedFrame, out int width, out int height, out byte[] pixels, out List stateNames) { width = height = 0; pixels = Array.Empty(); stateNames = new List(); if (!File.Exists(path)) return false; byte[] bytes; try { bytes = File.ReadAllBytes(path); } catch { return false; } if (!RuntimeRasterDecoder.TryDecode(bytes, out int sheetWidth, out int sheetHeight, out var sheet, out _)) return false; var metadata = new CraftIconFile(path); metadata.Load(); stateNames = metadata.States.Select(item => item.Name).Distinct(StringComparer.Ordinal).ToList(); var selected = stateName == null ? metadata.States.FirstOrDefault(item => item.Name.Length == 0) ?? metadata.States.FirstOrDefault() : metadata.States.FirstOrDefault(item => item.Name == stateName); if (selected == null) { if (metadata.States.Count > 0 && stateName != null) return false; width = sheetWidth; height = sheetHeight; pixels = sheet; return true; } int direction = DirectionIndex(requestedDir, selected.DirCount); int frame = requestedFrame <= 0 ? 0 : requestedFrame - 1; if (direction < 0 || frame < 0 || direction >= selected.DirCount || frame >= selected.FrameCount) return false; int stateTop = 0; foreach (var state in metadata.States) { if (ReferenceEquals(state, selected)) break; stateTop += state.Height * state.DirCount; } width = selected.Width; height = selected.Height; int left = frame * width; int top = stateTop + direction * height; if (left < 0 || top < 0 || left + width > sheetWidth || top + height > sheetHeight) return false; pixels = new byte[checked(width * height * 4)]; for (int row = 0; row < height; row++) Buffer.BlockCopy(sheet, ((top + row) * sheetWidth + left) * 4, pixels, row * width * 4, width * 4); return true; } public static int GetSelectedDirectionIndex(int direction, int directionCount) => DirectionIndex(direction, directionCount); private static int DirectionIndex(int direction, int directionCount) { if (directionCount <= 1) return 0; if (direction == 0) return 0; int[] order = directionCount >= 8 ? [2, 1, 4, 8, 6, 10, 5, 9] : [2, 1, 4, 8]; int limit = directionCount >= 8 ? 8 : directionCount >= 4 ? 4 : directionCount; int index = Array.IndexOf(order, direction); return index >= 0 && index < limit ? index : -1; } }