NsoExecutable.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using LibHac.Common.FixedArrays;
  2. using LibHac.Fs;
  3. using LibHac.Loader;
  4. using LibHac.Tools.FsSystem;
  5. using Ryujinx.Common.Logging;
  6. using System;
  7. using System.Text;
  8. using System.Text.RegularExpressions;
  9. namespace Ryujinx.HLE.Loaders.Executables
  10. {
  11. partial class NsoExecutable : IExecutable
  12. {
  13. public byte[] Program { get; }
  14. public Span<byte> Text => Program.AsSpan((int)TextOffset, (int)TextSize);
  15. public Span<byte> Ro => Program.AsSpan((int)RoOffset, (int)RoSize);
  16. public Span<byte> Data => Program.AsSpan((int)DataOffset, (int)DataSize);
  17. public uint TextOffset { get; }
  18. public uint RoOffset { get; }
  19. public uint DataOffset { get; }
  20. public uint BssOffset => DataOffset + (uint)Data.Length;
  21. public uint TextSize { get; }
  22. public uint RoSize { get; }
  23. public uint DataSize { get; }
  24. public uint BssSize { get; }
  25. public string Name;
  26. public Array32<byte> BuildId;
  27. [GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
  28. private static partial Regex ModuleRegex();
  29. [GeneratedRegex(@"sdk_version: ([0-9.]*)")]
  30. private static partial Regex FsSdkRegex();
  31. [GeneratedRegex(@"SDK MW[ -~]*")]
  32. private static partial Regex SdkMwRegex();
  33. public NsoExecutable(IStorage inStorage, string name = null)
  34. {
  35. NsoReader reader = new NsoReader();
  36. reader.Initialize(inStorage.AsFile(OpenMode.Read)).ThrowIfFailure();
  37. TextOffset = reader.Header.Segments[0].MemoryOffset;
  38. RoOffset = reader.Header.Segments[1].MemoryOffset;
  39. DataOffset = reader.Header.Segments[2].MemoryOffset;
  40. BssSize = reader.Header.BssSize;
  41. reader.GetSegmentSize(NsoReader.SegmentType.Data, out uint uncompressedSize).ThrowIfFailure();
  42. Program = new byte[DataOffset + uncompressedSize];
  43. TextSize = DecompressSection(reader, NsoReader.SegmentType.Text, TextOffset);
  44. RoSize = DecompressSection(reader, NsoReader.SegmentType.Ro, RoOffset);
  45. DataSize = DecompressSection(reader, NsoReader.SegmentType.Data, DataOffset);
  46. Name = name;
  47. BuildId = reader.Header.ModuleId;
  48. PrintRoSectionInfo();
  49. }
  50. private uint DecompressSection(NsoReader reader, NsoReader.SegmentType segmentType, uint offset)
  51. {
  52. reader.GetSegmentSize(segmentType, out uint uncompressedSize).ThrowIfFailure();
  53. var span = Program.AsSpan((int)offset, (int)uncompressedSize);
  54. reader.ReadSegment(segmentType, span).ThrowIfFailure();
  55. return uncompressedSize;
  56. }
  57. private void PrintRoSectionInfo()
  58. {
  59. string rawTextBuffer = Encoding.ASCII.GetString(Ro);
  60. StringBuilder stringBuilder = new StringBuilder();
  61. string modulePath = null;
  62. if (BitConverter.ToInt32(Ro.Slice(0, 4)) == 0)
  63. {
  64. int length = BitConverter.ToInt32(Ro.Slice(4, 4));
  65. if (length > 0)
  66. {
  67. modulePath = Encoding.UTF8.GetString(Ro.Slice(8, length));
  68. }
  69. }
  70. if (string.IsNullOrEmpty(modulePath))
  71. {
  72. Match moduleMatch = ModuleRegex().Match(rawTextBuffer);
  73. if (moduleMatch.Success)
  74. {
  75. modulePath = moduleMatch.Value;
  76. }
  77. }
  78. stringBuilder.AppendLine($" Module: {modulePath}");
  79. Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer);
  80. if (fsSdkMatch.Success)
  81. {
  82. stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", "")}");
  83. }
  84. MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer);
  85. if (sdkMwMatches.Count != 0)
  86. {
  87. string libHeader = " SDK Libraries: ";
  88. string libContent = string.Join($"\n{new string(' ', libHeader.Length)}", sdkMwMatches);
  89. stringBuilder.AppendLine($"{libHeader}{libContent}");
  90. }
  91. if (stringBuilder.Length > 0)
  92. {
  93. Logger.Info?.Print(LogClass.Loader, $"{Name}:\n{stringBuilder.ToString().TrimEnd('\r', '\n')}");
  94. }
  95. }
  96. }
  97. }