summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/archive.cpp
blob: 24b2262bc0aafe337ec97e17c81a01e6a50b4e19 (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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.

#include "common/common_types.h"
#include "common/file_util.h"
#include "common/math_util.h"

#include "core/file_sys/archive.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/hle/service/service.h"
#include "core/hle/kernel/archive.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace

namespace Kernel {

// Command to access archive file
enum class FileCommand : u32 {
    Dummy1          = 0x000100C6,
    Control         = 0x040100C4,
    OpenSubFile     = 0x08010100,
    Read            = 0x080200C2,
    Write           = 0x08030102,
    GetSize         = 0x08040000,
    SetSize         = 0x08050080,
    GetAttributes   = 0x08060000,
    SetAttributes   = 0x08070040,
    Close           = 0x08080000,
    Flush           = 0x08090000,
};

class Archive : public Object {
public:
    std::string GetTypeName() const { return "Archive"; }
    std::string GetName() const { return name; }

    static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
    Kernel::HandleType GetHandleType() const { return HandleType::Archive; }

    std::string name;           ///< Name of archive (optional)
    FileSys::Archive* backend;  ///< Archive backend interface

    /**
     * Synchronize kernel object 
     * @param wait Boolean wait set if current thread should wait as a result of sync operation
     * @return Result of operation, 0 on success, otherwise error code
     */
    Result SyncRequest(bool* wait) {
        u32* cmd_buff = Service::GetCommandBuffer();
        FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
        
        switch (cmd) {
        // Read from archive...
        case FileCommand::Read:
        {
            u64 offset  = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
            u32 length  = cmd_buff[3];
            u32 address = cmd_buff[5];

            // Number of bytes read
            cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
            break;
        }
        // Write to archive...
        case FileCommand::Write:
        {
            u64 offset  = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
            u32 length  = cmd_buff[3];
            u32 flush   = cmd_buff[4];
            u32 address = cmd_buff[6];

            // Number of bytes written
            cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
            break;
        }
        case FileCommand::GetSize:
        {
            u64 filesize = (u64) backend->GetSize();
            cmd_buff[2]  = (u32) filesize;         // Lower word
            cmd_buff[3]  = (u32) (filesize >> 32); // Upper word
            break;
        }
        case FileCommand::SetSize:
        {
            backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
            break;
        }
        // Unknown command...
        default:
        {
            ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
            return -1;
        }
        }
        cmd_buff[1] = 0; // No error
        return 0;
    }

    /**
     * Wait for kernel object to synchronize
     * @param wait Boolean wait set if current thread should wait as a result of sync operation
     * @return Result of operation, 0 on success, otherwise error code
     */
    Result WaitSynchronization(bool* wait) {
        // TODO(bunnei): ImplementMe
        ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
        return 0;
    }
};

class File : public Object {
public:
    std::string GetTypeName() const { return "File"; }
    std::string GetName() const { return path; }

    static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
    Kernel::HandleType GetHandleType() const { return HandleType::File; }

    std::string path; ///< Path of the file
    std::unique_ptr<FileSys::File> backend; ///< File backend interface

    /**
     * Synchronize kernel object
     * @param wait Boolean wait set if current thread should wait as a result of sync operation
     * @return Result of operation, 0 on success, otherwise error code
     */
    Result SyncRequest(bool* wait) {
        u32* cmd_buff = Service::GetCommandBuffer();
        FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
        switch (cmd) {

        // Read from file...
        case FileCommand::Read:
        {
            u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
            u32 length  = cmd_buff[3];
            u32 address = cmd_buff[5];
            DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%x length=%d address=0x%x",
                      GetTypeName().c_str(), GetName().c_str(), offset, length, address);
            cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
            break;
        }

        // Write to file...
        case FileCommand::Write:
        {
            u64 offset  = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
            u32 length  = cmd_buff[3];
            u32 flush   = cmd_buff[4];
            u32 address = cmd_buff[6];
            DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%x length=%d address=0x%x, flush=0x%x",
                      GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
            cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
            break;
        }

        case FileCommand::GetSize:
        {
            DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
            u64 size = backend->GetSize();
            cmd_buff[2] = (u32)size;
            cmd_buff[3] = size >> 32;
            break;
        }

        // Unknown command...
        default:
            ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
            cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
            return -1;
        }
        cmd_buff[1] = 0; // No error
        return 0;
    }

    /**
     * Wait for kernel object to synchronize
     * @param wait Boolean wait set if current thread should wait as a result of sync operation
     * @return Result of operation, 0 on success, otherwise error code
     */
    Result WaitSynchronization(bool* wait) {
        // TODO(bunnei): ImplementMe
        ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
        return 0;
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////

std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode

/**
 * Opens an archive
 * @param id_code IdCode of the archive to open
 * @return Handle to archive if it exists, otherwise a null handle (0)
 */
Handle OpenArchive(FileSys::Archive::IdCode id_code) {
    auto itr = g_archive_map.find(id_code);
    if (itr == g_archive_map.end()) {
        return 0;
    }
    return itr->second;
}

/**
 * Mounts an archive
 * @param archive Pointer to the archive to mount
 * @return Result of operation, 0 on success, otherwise error code
 */
Result MountArchive(Archive* archive) {
    FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
    if (0 != OpenArchive(id_code)) {
        ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
        return -1;
    }
    g_archive_map[id_code] = archive->GetHandle();
    INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
    return 0;
}

/**
 * Creates an Archive
 * @param handle Handle to newly created archive object
 * @param backend File system backend interface to the archive
 * @param name Optional name of Archive
 * @return Newly created Archive object
 */
Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
    Archive* archive = new Archive;
    handle = Kernel::g_object_pool.Create(archive);
    archive->name = name;
    archive->backend = backend;

    MountArchive(archive);
    
    return archive;
}

/**
 * Creates an Archive
 * @param backend File system backend interface to the archive
 * @param name Optional name of Archive
 * @return Handle to newly created Archive object
 */
Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
    Handle handle;
    CreateArchive(handle, backend, name);
    return handle;
}

/**
 * Open a File from an Archive
 * @param archive_handle Handle to an open Archive object
 * @param path Path to the File inside of the Archive
 * @param mode Mode under which to open the File
 * @return Opened File object
 */
Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
    File* file = new File;
    Handle handle = Kernel::g_object_pool.Create(file);

    Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
    file->path = path;
    file->backend = archive->backend->OpenFile(path, mode);

    return handle;
}

/// Initialize archives
void ArchiveInit() {
    g_archive_map.clear();

    // TODO(Link Mauve): Add the other archive types (see here for the known types:
    // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).  Currently the only half-finished
    // archive type is SDMC, so it is the only one getting exposed.

    // TODO(Link Mauve): don't assume the path separator is '/'.
    std::string sdmc_directory = FileUtil::GetCurrentDir() + "/userdata/sdmc";
    auto archive = new FileSys::Archive_SDMC(sdmc_directory);
    if (archive->Initialize())
        CreateArchive(archive, "SDMC");
    else
        ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
}

/// Shutdown archives
void ArchiveShutdown() {
    g_archive_map.clear();
}

} // namespace Kernel