aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs
blob: c0ffe767ca5c9793b3c08425e76d1454b9559051 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
using Ryujinx.HLE.HOS.Ipc;
using System;
using System.Collections.Generic;
using System.Text;

namespace Ryujinx.HLE.HOS.Services.FspSrv
{
    class IDirectory : IpcService, IDisposable
    {
        private const int DirectoryEntrySize = 0x310;

        private Dictionary<int, ServiceProcessRequest> _commands;

        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;

        private IEnumerator<LibHac.Fs.DirectoryEntry> _enumerator;

        public event EventHandler<EventArgs> Disposed;

        public string Path { get; }

        private LibHac.Fs.IDirectory _provider;

        public IDirectory(LibHac.Fs.IDirectory directory)
        {
            _commands = new Dictionary<int, ServiceProcessRequest>
            {
                { 0, Read          },
                { 1, GetEntryCount }
            };

            _provider = directory;

            Path = directory.FullPath;

            _enumerator = directory.Read().GetEnumerator();
        }

        // Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries)
        public long Read(ServiceCtx context)
        {
            long bufferPosition = context.Request.ReceiveBuff[0].Position;
            long bufferLen      = context.Request.ReceiveBuff[0].Size;

            int maxReadCount = (int)(bufferLen / DirectoryEntrySize);
            int readCount    = 0;

            while (readCount < maxReadCount && _enumerator.MoveNext())
            {
                long position = bufferPosition + readCount * DirectoryEntrySize;

                WriteDirectoryEntry(context, position, _enumerator.Current);

                readCount++;
            }

            context.ResponseData.Write((long)readCount);

            return 0;
        }

        private void WriteDirectoryEntry(ServiceCtx context, long position, LibHac.Fs.DirectoryEntry entry)
        {
            for (int offset = 0; offset < 0x300; offset += 8)
            {
                context.Memory.WriteInt64(position + offset, 0);
            }

            byte[] nameBuffer = Encoding.UTF8.GetBytes(entry.Name);

            context.Memory.WriteBytes(position, nameBuffer);

            context.Memory.WriteInt32(position + 0x300, (int)entry.Attributes);
            context.Memory.WriteInt32(position + 0x304, (byte)entry.Type);
            context.Memory.WriteInt64(position + 0x308, entry.Size);
        }

        // GetEntryCount() -> u64
        public long GetEntryCount(ServiceCtx context)
        {
            context.ResponseData.Write((long)_provider.GetEntryCount());

            return 0;
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                Disposed?.Invoke(this, EventArgs.Empty);
            }
        }
    }
}