diff --git a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index 460d69cb1..f8c102f00 100644 Binary files a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h b/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h index ea350fbc8..8245a19f2 100644 --- a/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h +++ b/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h @@ -21,7 +21,10 @@ struct GameInfo { long TitleId; char Developer[256]; int Version; + unsigned char* ImageData; + unsigned int ImageSize; }; + extern struct GameInfo get_game_info(int, char*); // Declare the main_ryujinx_sdl function, matching the signature int main_ryujinx_sdl(int argc, char **argv); diff --git a/src/MeloNX/MeloNX/Models/Game.swift b/src/MeloNX/MeloNX/Models/Game.swift index e5f0daf0d..cc11c5aa5 100644 --- a/src/MeloNX/MeloNX/Models/Game.swift +++ b/src/MeloNX/MeloNX/Models/Game.swift @@ -20,5 +20,26 @@ public struct Game: Identifiable, Equatable { var titleId: String var developer: String var version: String - var icon: Image? + var icon: UIImage? + + func createImage(from gameInfo: GameInfo) -> UIImage? { + // Access the struct + let gameInfoValue = gameInfo + + // Get the image data + let imageSize = Int(gameInfoValue.ImageSize) + guard imageSize > 0, imageSize <= 1024 * 1024 else { + print("Invalid image size.") + return nil + } + + // Convert the ImageData byte array to Swift's Data + let imageData = Data(bytes: gameInfoValue.ImageData, count: imageSize) + + // Create a UIImage (or NSImage on macOS) + + print(imageData) + + return UIImage(data: imageData) + } } diff --git a/src/MeloNX/MeloNX/Views/GamesList/GameListView.swift b/src/MeloNX/MeloNX/Views/GamesList/GameListView.swift index 28339debd..75c9a0736 100644 --- a/src/MeloNX/MeloNX/Views/GamesList/GameListView.swift +++ b/src/MeloNX/MeloNX/Views/GamesList/GameListView.swift @@ -205,22 +205,24 @@ struct GameLibraryView: View { var game = Game(containerFolder: romsDirectory, fileType: .item, fileURL: fileURLCandidate, titleName: "", titleId: "", developer: "", version: "") game.titleName = withUnsafePointer(to: &gameInfo.TitleName) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { - String(cString: $0) - } - } - + $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { + String(cString: $0) + } + } + game.developer = withUnsafePointer(to: &gameInfo.Developer) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { - String(cString: $0) - } - } + $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { + String(cString: $0) + } + } game.titleId = String(gameInfo.TitleId) game.version = String(gameInfo.Version) + game.icon = game.createImage(from: gameInfo) + games.append(game) } catch { @@ -274,7 +276,7 @@ struct RecentGameCard: View { }) { VStack(alignment: .leading, spacing: 8) { if let icon = game.icon { - icon + Image(uiImage: icon) .resizable() .aspectRatio(contentMode: .fill) .frame(width: 140, height: 140) @@ -321,7 +323,7 @@ struct GameListRow: View { HStack(spacing: 16) { // Game Icon if let icon = game.icon { - icon + Image(uiImage: icon) .resizable() .aspectRatio(contentMode: .fill) .frame(width: 45, height: 45) diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 1315c5873..77cabc9a5 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -273,7 +273,7 @@ namespace Ryujinx.Headless.SDL2 var gameInfo = GetGameInfo(stream, extension); - return new GameInfoNative(0, gameInfo.TitleName, 0, gameInfo.Developer, 0); + return new GameInfoNative(0, gameInfo.TitleName, 0, gameInfo.Developer, 0, gameInfo.Icon); } public static GameInfo? GetGameInfo(Stream gameStream, string extension) @@ -1376,7 +1376,7 @@ namespace Ryujinx.Headless.SDL2 return new FileStream(safeHandle, FileAccess.ReadWrite); } - public class GameInfo + public class GameInfo { public double FileSize; public string? TitleName; @@ -1386,33 +1386,63 @@ namespace Ryujinx.Headless.SDL2 public byte[]? Icon; } - public unsafe struct GameInfoNative +public unsafe struct GameInfoNative +{ + public ulong FileSize; + public fixed byte TitleName[512]; + public ulong TitleId; + public fixed byte Developer[256]; + public uint Version; + public byte* ImageData; // Changed to pointer + public uint ImageSize; // Actual size of image data + + public GameInfoNative(ulong fileSize, string titleName, ulong titleId, string developer, uint version, byte[] imageData) { - public ulong FileSize; - public fixed byte TitleName[512]; - public ulong TitleId; - public fixed byte Developer[256]; - public uint Version; + FileSize = fileSize; + TitleId = titleId; + Version = version; - public GameInfoNative(ulong fileSize, string titleName, ulong titleId, string developer, uint version) + fixed (byte* developerPtr = Developer) + fixed (byte* titleNamePtr = TitleName) { - FileSize = fileSize; - TitleId = titleId; - Version = version; - - fixed (byte* developerPtr = Developer) - fixed (byte* titleNamePtr = TitleName) - { - CopyStringToFixedArray(titleName, titleNamePtr, 512); - CopyStringToFixedArray(developer, developerPtr, 256); - } + CopyStringToFixedArray(titleName, titleNamePtr, 512); + CopyStringToFixedArray(developer, developerPtr, 256); } - private void CopyStringToFixedArray(string source, byte* destination, int length) + if (imageData == null || imageData.Length > 1024 * 1024) + { + throw new ArgumentException("Image data must not exceed 1 MB."); + } + + ImageSize = (uint)imageData.Length; + + // Allocate unmanaged memory for the image data + ImageData = (byte*)Marshal.AllocHGlobal(imageData.Length); + + // Copy the image data to the allocated memory + Marshal.Copy(imageData, 0, (IntPtr)ImageData, imageData.Length); + } + + // Don't forget to free the allocated memory + public void Dispose() + { + if (ImageData != null) + { + Marshal.FreeHGlobal((IntPtr)ImageData); + ImageData = null; + } + } + private static void CopyStringToFixedArray(string source, byte* destination, int length) { var span = new Span(destination, length); Encoding.UTF8.GetBytes(source, span); } + + private static void CopyArrayToFixedArray(byte[] source, byte* destination, int maxLength) + { + var span = new Span(destination, maxLength); + source.AsSpan().CopyTo(span); + } } } }