NsoExecutable.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. using LibHac.Common;
  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. 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 Buffer32 BuildId;
  27. public NsoExecutable(IStorage inStorage, string name = null)
  28. {
  29. NsoReader reader = new NsoReader();
  30. reader.Initialize(inStorage.AsFile(OpenMode.Read)).ThrowIfFailure();
  31. TextOffset = reader.Header.Segments[0].MemoryOffset;
  32. RoOffset = reader.Header.Segments[1].MemoryOffset;
  33. DataOffset = reader.Header.Segments[2].MemoryOffset;
  34. BssSize = reader.Header.BssSize;
  35. reader.GetSegmentSize(NsoReader.SegmentType.Data, out uint uncompressedSize).ThrowIfFailure();
  36. Program = new byte[DataOffset + uncompressedSize];
  37. TextSize = DecompressSection(reader, NsoReader.SegmentType.Text, TextOffset);
  38. RoSize = DecompressSection(reader, NsoReader.SegmentType.Ro, RoOffset);
  39. DataSize = DecompressSection(reader, NsoReader.SegmentType.Data, DataOffset);
  40. Name = name;
  41. BuildId = reader.Header.ModuleId;
  42. PrintRoSectionInfo();
  43. }
  44. private uint DecompressSection(NsoReader reader, NsoReader.SegmentType segmentType, uint offset)
  45. {
  46. reader.GetSegmentSize(segmentType, out uint uncompressedSize).ThrowIfFailure();
  47. var span = Program.AsSpan((int)offset, (int)uncompressedSize);
  48. reader.ReadSegment(segmentType, span).ThrowIfFailure();
  49. return uncompressedSize;
  50. }
  51. private void PrintRoSectionInfo()
  52. {
  53. string rawTextBuffer = Encoding.ASCII.GetString(Ro);
  54. StringBuilder stringBuilder = new StringBuilder();
  55. string modulePath = null;
  56. if (BitConverter.ToInt32(Ro.Slice(0, 4)) == 0)
  57. {
  58. int length = BitConverter.ToInt32(Ro.Slice(4, 4));
  59. if (length > 0)
  60. {
  61. modulePath = Encoding.UTF8.GetString(Ro.Slice(8, length));
  62. }
  63. }
  64. if (string.IsNullOrEmpty(modulePath))
  65. {
  66. Match moduleMatch = Regex.Match(rawTextBuffer, @"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
  67. if (moduleMatch.Success)
  68. {
  69. modulePath = moduleMatch.Value;
  70. }
  71. }
  72. stringBuilder.AppendLine($" Module: {modulePath}");
  73. Match fsSdkMatch = Regex.Match(rawTextBuffer, @"sdk_version: ([0-9.]*)", RegexOptions.Compiled);
  74. if (fsSdkMatch.Success)
  75. {
  76. stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", "")}");
  77. }
  78. MatchCollection sdkMwMatches = Regex.Matches(rawTextBuffer, @"SDK MW[ -~]*", RegexOptions.Compiled);
  79. if (sdkMwMatches.Count != 0)
  80. {
  81. string libHeader = " SDK Libraries: ";
  82. string libContent = string.Join($"\n{new string(' ', libHeader.Length)}", sdkMwMatches);
  83. stringBuilder.AppendLine($"{libHeader}{libContent}");
  84. }
  85. if (stringBuilder.Length > 0)
  86. {
  87. Logger.Info?.Print(LogClass.Loader, $"{Name}:\n{stringBuilder.ToString().TrimEnd('\r', '\n')}");
  88. }
  89. }
  90. }
  91. }