diff options
author | emmauss <emmausssss@gmail.com> | 2018-11-18 21:37:41 +0200 |
---|---|---|
committer | gdkchan <gab.dark.100@gmail.com> | 2018-11-18 17:37:41 -0200 |
commit | fe8fbb6fb9b85a528ddfa4848ec8e35fd9a5e9a5 (patch) | |
tree | 7cd33a50d1ebc98e467df93aeb86315679110778 /Ryujinx.HLE/FileSystem/Content/ContentManager.cs | |
parent | e603b7afbcdff0fc732304872f5a65d410c601f9 (diff) |
Implement ContentManager and related services (#438)
* Implement contentmanager and related services
* small changes
* read system firmware version from nand
* add pfs support, write directoryentry info for romfs files
* add file check in fsp-srv:8
* add support for open fs of internal files
* fix filename when accessing pfs
* use switch style paths for contentpath
* close nca after verifying type
* removed publishing profiles, align directory entry
* fix style
* lots of style fixes
* yasf(yet another style fix)
* yasf(yet another style fix) plus symbols
* enforce path check on every fs access
* change enum type to default
* fix typo
Diffstat (limited to 'Ryujinx.HLE/FileSystem/Content/ContentManager.cs')
-rw-r--r-- | Ryujinx.HLE/FileSystem/Content/ContentManager.cs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs new file mode 100644 index 00000000..025eb261 --- /dev/null +++ b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs @@ -0,0 +1,300 @@ +using LibHac; +using Ryujinx.HLE.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Ryujinx.HLE.FileSystem.Content +{ + internal class ContentManager + { + private Dictionary<StorageId, LinkedList<LocationEntry>> LocationEntries; + + private Dictionary<string, long> SharedFontTitleDictionary; + + private SortedDictionary<(ulong, ContentType), string> ContentDictionary; + + private Switch Device; + + public ContentManager(Switch Device) + { + ContentDictionary = new SortedDictionary<(ulong, ContentType), string>(); + LocationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>(); + + SharedFontTitleDictionary = new Dictionary<string, long>() + { + { "FontStandard", 0x0100000000000811 }, + { "FontChineseSimplified", 0x0100000000000814 }, + { "FontExtendedChineseSimplified", 0x0100000000000814 }, + { "FontKorean", 0x0100000000000812 }, + { "FontChineseTraditional", 0x0100000000000813 }, + { "FontNintendoExtended" , 0x0100000000000810 }, + }; + + this.Device = Device; + } + + public void LoadEntries() + { + ContentDictionary = new SortedDictionary<(ulong, ContentType), string>(); + + foreach (StorageId StorageId in Enum.GetValues(typeof(StorageId))) + { + string ContentDirectory = null; + string ContentPathString = null; + string RegisteredDirectory = null; + + try + { + ContentPathString = LocationHelper.GetContentRoot(StorageId); + ContentDirectory = LocationHelper.GetRealPath(Device.FileSystem, ContentPathString); + RegisteredDirectory = Path.Combine(ContentDirectory, "registered"); + } + catch (NotSupportedException NEx) + { + continue; + } + + Directory.CreateDirectory(RegisteredDirectory); + + LinkedList<LocationEntry> LocationList = new LinkedList<LocationEntry>(); + + void AddEntry(LocationEntry Entry) + { + LocationList.AddLast(Entry); + } + + foreach (string DirectoryPath in Directory.EnumerateDirectories(RegisteredDirectory)) + { + if (Directory.GetFiles(DirectoryPath).Length > 0) + { + string NcaName = new DirectoryInfo(DirectoryPath).Name.Replace(".nca", string.Empty); + + using (FileStream NcaFile = new FileStream(Directory.GetFiles(DirectoryPath)[0], FileMode.Open, FileAccess.Read)) + { + Nca Nca = new Nca(Device.System.KeySet, NcaFile, false); + + string SwitchPath = Path.Combine(ContentPathString + ":", + NcaFile.Name.Replace(ContentDirectory, string.Empty).TrimStart('\\')); + + // Change path format to switch's + SwitchPath = SwitchPath.Replace('\\', '/'); + + LocationEntry Entry = new LocationEntry(SwitchPath, + 0, + (long)Nca.Header.TitleId, + Nca.Header.ContentType); + + AddEntry(Entry); + + ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName); + + NcaFile.Close(); + Nca.Dispose(); + NcaFile.Dispose(); + } + } + } + + foreach (string FilePath in Directory.EnumerateFiles(ContentDirectory)) + { + if (Path.GetExtension(FilePath) == ".nca") + { + string NcaName = Path.GetFileNameWithoutExtension(FilePath); + + using (FileStream NcaFile = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) + { + Nca Nca = new Nca(Device.System.KeySet, NcaFile, false); + + string SwitchPath = Path.Combine(ContentPathString + ":", + FilePath.Replace(ContentDirectory, string.Empty).TrimStart('\\')); + + // Change path format to switch's + SwitchPath = SwitchPath.Replace('\\', '/'); + + LocationEntry Entry = new LocationEntry(SwitchPath, + 0, + (long)Nca.Header.TitleId, + Nca.Header.ContentType); + + AddEntry(Entry); + + ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName); + + NcaFile.Close(); + Nca.Dispose(); + NcaFile.Dispose(); + } + } + } + + if(LocationEntries.ContainsKey(StorageId) && LocationEntries[StorageId]?.Count == 0) + { + LocationEntries.Remove(StorageId); + } + + if (!LocationEntries.ContainsKey(StorageId)) + { + LocationEntries.Add(StorageId, LocationList); + } + } + } + + public void ClearEntry(long TitleId, ContentType ContentType,StorageId StorageId) + { + RemoveLocationEntry(TitleId, ContentType, StorageId); + } + + public void RefreshEntries(StorageId StorageId, int Flag) + { + LinkedList<LocationEntry> LocationList = LocationEntries[StorageId]; + LinkedListNode<LocationEntry> LocationEntry = LocationList.First; + + while (LocationEntry != null) + { + LinkedListNode<LocationEntry> NextLocationEntry = LocationEntry.Next; + + if (LocationEntry.Value.Flag == Flag) + { + LocationList.Remove(LocationEntry.Value); + } + + LocationEntry = NextLocationEntry; + } + } + + public bool HasNca(string NcaId, StorageId StorageId) + { + if (ContentDictionary.ContainsValue(NcaId)) + { + var Content = ContentDictionary.FirstOrDefault(x => x.Value == NcaId); + long TitleId = (long)Content.Key.Item1; + ContentType ContentType = Content.Key.Item2; + StorageId Storage = GetInstalledStorage(TitleId, ContentType, StorageId); + + return Storage == StorageId; + } + + return false; + } + + public UInt128 GetInstalledNcaId(long TitleId, ContentType ContentType) + { + if (ContentDictionary.ContainsKey(((ulong)TitleId,ContentType))) + { + return new UInt128(ContentDictionary[((ulong)TitleId,ContentType)]); + } + + return new UInt128(); + } + + public StorageId GetInstalledStorage(long TitleId, ContentType ContentType, StorageId StorageId) + { + LocationEntry LocationEntry = GetLocation(TitleId, ContentType, StorageId); + + return LocationEntry.ContentPath != null ? + LocationHelper.GetStorageId(LocationEntry.ContentPath) : StorageId.None; + } + + public string GetInstalledContentPath(long TitleId, StorageId StorageId, ContentType ContentType) + { + LocationEntry LocationEntry = GetLocation(TitleId, ContentType, StorageId); + + if (VerifyContentType(LocationEntry, ContentType)) + { + return LocationEntry.ContentPath; + } + + return string.Empty; + } + + public void RedirectLocation(LocationEntry NewEntry, StorageId StorageId) + { + LocationEntry LocationEntry = GetLocation(NewEntry.TitleId, NewEntry.ContentType, StorageId); + + if (LocationEntry.ContentPath != null) + { + RemoveLocationEntry(NewEntry.TitleId, NewEntry.ContentType, StorageId); + } + + AddLocationEntry(NewEntry, StorageId); + } + + private bool VerifyContentType(LocationEntry LocationEntry, ContentType ContentType) + { + StorageId StorageId = LocationHelper.GetStorageId(LocationEntry.ContentPath); + string InstalledPath = Device.FileSystem.SwitchPathToSystemPath(LocationEntry.ContentPath); + + if (!string.IsNullOrWhiteSpace(InstalledPath)) + { + if (File.Exists(InstalledPath)) + { + FileStream File = new FileStream(InstalledPath, FileMode.Open, FileAccess.Read); + Nca Nca = new Nca(Device.System.KeySet, File, false); + bool ContentCheck = Nca.Header.ContentType == ContentType; + + Nca.Dispose(); + File.Dispose(); + + return ContentCheck; + } + } + + return false; + } + + private void AddLocationEntry(LocationEntry Entry, StorageId StorageId) + { + LinkedList<LocationEntry> LocationList = null; + + if (LocationEntries.ContainsKey(StorageId)) + { + LocationList = LocationEntries[StorageId]; + } + + if (LocationList != null) + { + if (LocationList.Contains(Entry)) + { + LocationList.Remove(Entry); + } + + LocationList.AddLast(Entry); + } + } + + private void RemoveLocationEntry(long TitleId, ContentType ContentType, StorageId StorageId) + { + LinkedList<LocationEntry> LocationList = null; + + if (LocationEntries.ContainsKey(StorageId)) + { + LocationList = LocationEntries[StorageId]; + } + + if (LocationList != null) + { + LocationEntry Entry = + LocationList.ToList().Find(x => x.TitleId == TitleId && x.ContentType == ContentType); + + if (Entry.ContentPath != null) + { + LocationList.Remove(Entry); + } + } + } + + public bool TryGetFontTitle(string FontName, out long TitleId) + { + return SharedFontTitleDictionary.TryGetValue(FontName, out TitleId); + } + + private LocationEntry GetLocation(long TitleId, ContentType ContentType,StorageId StorageId) + { + LinkedList<LocationEntry> LocationList = LocationEntries[StorageId]; + + return LocationList.ToList().Find(x => x.TitleId == TitleId && x.ContentType == ContentType); + } + } +} |