aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/service/boss/online_service.cpp
blob: d289c910b632d02fd470c5a2b807bad824378b1d (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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <boost/serialization/map.hpp>
#include "common/archives.h"
#include "common/file_util.h"
#include "common/serialization/boost_std_variant.hpp"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/file_backend.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/boss/online_service.h"

namespace Service::BOSS {

namespace ErrCodes {
enum {
    TaskNotFound = 51,
    NsDataNotFound = 64,
    UnknownPropertyID = 77,
};
}

OnlineService::OnlineService(u64 program_id_, u64 extdata_id_)
    : program_id(program_id_), extdata_id(extdata_id_) {}

template <class Archive>
void OnlineService::serialize(Archive& ar, const unsigned int) {
    ar& current_props;
    ar& task_id_list;
    ar& program_id;
    ar& extdata_id;
}
SERIALIZE_IMPL(OnlineService)

template <class Archive>
void BossTaskProperties::serialize(Archive& ar, const unsigned int) {
    ar& task_result;
    ar& properties;
}
SERIALIZE_IMPL(BossTaskProperties)

Result OnlineService::InitializeSession(u64 init_program_id) {
    // The BOSS service uses three databases:
    // BOSS_A: Archive? A list of program ids and some properties that are keyed on program
    // BOSS_SS: Saved Strings? Includes the url and the other string properties, and also some other
    // properties?, keyed on task_id
    // BOSS_SV: Saved Values? Includes task id and most properties keyed on task_id

    // It saves data in its databases in the following format:
    // A four byte header (always 00 80 34 12?) followed by any number of 0x800(BOSS_A) and
    // 0xC00(BOSS_SS and BOSS_SV) entries.

    current_props = BossTaskProperties();

    if (init_program_id == 0) {
        init_program_id = program_id;
    }

    const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
    FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);

    // Open the SystemSaveData archive 0x00010034
    const FileSys::Path archive_path(boss_system_savedata_id);
    auto archive_result = systemsavedata_factory.Open(archive_path, 0);

    std::unique_ptr<FileSys::ArchiveBackend> boss_system_save_data_archive;
    if (archive_result.Succeeded()) {
        boss_system_save_data_archive = std::move(archive_result).Unwrap();
    } else if (archive_result.Code() == FileSys::ResultNotFound) {
        // If the archive didn't exist, create the files inside
        systemsavedata_factory.Format(archive_path, FileSys::ArchiveFormatInfo(), 0);

        // Open it again to get a valid archive now that the folder exists
        auto create_archive_result = systemsavedata_factory.Open(archive_path, 0);
        if (!create_archive_result.Succeeded()) {
            LOG_ERROR(Service_BOSS, "Could not open BOSS savedata");
            // TODO: Proper error code.
            return ResultUnknown;
        }
        boss_system_save_data_archive = std::move(create_archive_result).Unwrap();
    } else {
        LOG_ERROR(Service_BOSS, "Could not open BOSS savedata");
        // TODO: Proper error code.
        return ResultUnknown;
    }

    FileSys::Mode open_mode = {};
    open_mode.read_flag.Assign(1);

    // TODO: Read data from BOSS_A.db

    auto boss_sv_result =
        boss_system_save_data_archive->OpenFile(FileSys::Path("/BOSS_SV.db"), open_mode);
    auto boss_ss_result =
        boss_system_save_data_archive->OpenFile(FileSys::Path("/BOSS_SS.db"), open_mode);
    if (!boss_sv_result.Succeeded() || !boss_ss_result.Succeeded()) {
        LOG_ERROR(Service_BOSS, "Could not open BOSS database.");
        return ResultSuccess;
    }

    auto boss_sv = std::move(boss_sv_result).Unwrap();
    auto boss_ss = std::move(boss_ss_result).Unwrap();
    if (!(boss_sv->GetSize() > BOSS_SAVE_HEADER_SIZE &&
          ((boss_sv->GetSize() - BOSS_SAVE_HEADER_SIZE) % BOSS_S_ENTRY_SIZE) == 0 &&
          boss_sv->GetSize() == boss_ss->GetSize())) {
        LOG_ERROR(Service_BOSS, "BOSS database has incorrect size.");
        return ResultSuccess;
    }

    // Read the files if they already exist
    const u64 num_entries = (boss_sv->GetSize() - BOSS_SAVE_HEADER_SIZE) / BOSS_S_ENTRY_SIZE;
    for (u64 i = 0; i < num_entries; i++) {
        const u64 entry_offset = i * BOSS_S_ENTRY_SIZE + BOSS_SAVE_HEADER_SIZE;

        BossSVData sv_data;
        boss_sv->Read(entry_offset, sizeof(BossSVData), reinterpret_cast<u8*>(&sv_data));

        BossSSData ss_data;
        boss_ss->Read(entry_offset, sizeof(BossSSData), reinterpret_cast<u8*>(&ss_data));

        if (sv_data.program_id != init_program_id) {
            continue;
        }

        std::vector<u8> url_vector(URL_SIZE);
        std::memcpy(url_vector.data(), ss_data.url.data(), URL_SIZE);
        current_props.properties[PropertyID::Url] = url_vector;

        const auto task_id = std::string(sv_data.task_id.data());
        if (task_id_list.contains(task_id)) {
            LOG_WARNING(Service_BOSS, "TaskId already in list, will be replaced");
            task_id_list.erase(task_id);
        }

        task_id_list.emplace(task_id, std::move(current_props));
        current_props = BossTaskProperties();
    }

    return ResultSuccess;
}

void OnlineService::RegisterTask(const u32 size, Kernel::MappedBuffer& buffer) {
    std::string task_id(size, 0);
    buffer.Read(task_id.data(), 0, size);

    if (task_id_list.contains(task_id)) {
        LOG_WARNING(Service_BOSS, "TaskId already in list, will be replaced");
        task_id_list.erase(task_id);
    }
    task_id_list.emplace(task_id, std::move(current_props));
    current_props = BossTaskProperties();
}

Result OnlineService::UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer) {
    if (size > TASK_ID_SIZE) {
        LOG_WARNING(Service_BOSS, "TaskId cannot be longer than 8");
        // TODO: Proper error code.
        return ResultUnknown;
    }

    std::string task_id(size, 0);
    buffer.Read(task_id.data(), 0, size);
    if (task_id_list.erase(task_id) == 0) {
        LOG_WARNING(Service_BOSS, "TaskId '{}' not in list", task_id);
        return {ErrCodes::TaskNotFound, ErrorModule::BOSS, ErrorSummary::InvalidState,
                ErrorLevel::Status};
    }

    return ResultSuccess;
}

void OnlineService::GetTaskIdList() {
    current_props.properties[PropertyID::TotalTasks] = static_cast<u16>(task_id_list.size());

    const auto num_task_ids = TASKIDLIST_SIZE / TASK_ID_SIZE;
    std::vector<std::array<u8, TASK_ID_SIZE>> task_ids(num_task_ids);

    u16 num_returned_task_ids = 0;
    for (const auto& iter : task_id_list) {
        const std::string current_task_id = iter.first;
        if (current_task_id.size() > TASK_ID_SIZE || num_returned_task_ids >= num_task_ids) {
            LOG_WARNING(Service_BOSS, "TaskId {} too long or too many TaskIds", current_task_id);
            continue;
        }
        std::memcpy(task_ids[num_returned_task_ids++].data(), current_task_id.data(), TASK_ID_SIZE);
    }

    auto* task_list_prop =
        std::get_if<std::vector<u8>>(&current_props.properties[PropertyID::TaskIdList]);
    if (task_list_prop && task_list_prop->size() == TASKIDLIST_SIZE) {
        std::memcpy(task_list_prop->data(), task_ids.data(), TASKIDLIST_SIZE);
    }
}

FileSys::Path OnlineService::GetBossDataDir() {
    const u32 high = static_cast<u32>(extdata_id >> 32);
    const u32 low = static_cast<u32>(extdata_id & 0xFFFFFFFF);
    return FileSys::ConstructExtDataBinaryPath(1, high, low);
}

std::unique_ptr<FileSys::ArchiveBackend> OnlineService::OpenBossExtData() {
    FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory(
        FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::ExtSaveDataType::Boss);
    const FileSys::Path boss_path{GetBossDataDir()};
    auto archive_result = boss_extdata_archive_factory.Open(boss_path, 0);
    if (!archive_result.Succeeded()) {
        LOG_WARNING(Service_BOSS, "Failed to open SpotPass ext data archive with ID '{:#010x}'.",
                    extdata_id);
        return nullptr;
    }
    return std::move(archive_result).Unwrap();
}

std::vector<FileSys::Entry> OnlineService::GetBossExtDataFiles(
    FileSys::ArchiveBackend* boss_archive) {
    auto dir_result = boss_archive->OpenDirectory("/");
    if (!dir_result.Succeeded()) {
        LOG_WARNING(Service_BOSS,
                    "Failed to open root directory of SpotPass ext data with ID '{:#010x}'.",
                    extdata_id);
        return {};
    }
    auto dir = std::move(dir_result).Unwrap();

    // Keep reading the directory 32 files at a time until all files have been checked
    std::vector<FileSys::Entry> boss_files;
    constexpr u32 files_to_read = 32;
    u32 entry_count = 0;
    std::size_t i = 0;
    do {
        boss_files.resize(boss_files.size() + files_to_read);
        entry_count = dir->Read(files_to_read, boss_files.data() + (i++ * files_to_read));
    } while (entry_count > files_to_read);

    // Resize to trim off unused entries from the final read.
    boss_files.resize((i - 1) * files_to_read + entry_count);
    return boss_files;
}

std::vector<NsDataEntry> OnlineService::GetNsDataEntries() {
    auto boss_archive = OpenBossExtData();
    if (!boss_archive) {
        return {};
    }

    std::vector<NsDataEntry> ns_data;
    const auto boss_files = GetBossExtDataFiles(boss_archive.get());
    for (const auto& current_file : boss_files) {
        constexpr u32 boss_header_length = 0x34;
        if (current_file.is_directory || current_file.file_size < boss_header_length) {
            LOG_WARNING(Service_BOSS,
                        "SpotPass extdata contains directory or file is too short: '{}'",
                        Common::UTF16ToUTF8(current_file.filename));
            continue;
        }

        FileSys::Mode mode{};
        mode.read_flag.Assign(1);

        NsDataEntry entry{.filename = Common::UTF16ToUTF8(current_file.filename)};
        auto file_result = boss_archive->OpenFile("/" + entry.filename, mode);
        if (!file_result.Succeeded()) {
            LOG_WARNING(Service_BOSS, "Opening SpotPass file failed.");
            continue;
        }

        auto file = std::move(file_result).Unwrap();
        file->Read(0, boss_header_length, reinterpret_cast<u8*>(&entry.header));
        if (entry.header.header_length != BOSS_EXTDATA_HEADER_LENGTH) {
            LOG_WARNING(
                Service_BOSS,
                "Incorrect header length or non-SpotPass file; expected {:#010x}, found {:#010x}",
                BOSS_EXTDATA_HEADER_LENGTH, entry.header.header_length);
            continue;
        }

        if (entry.header.program_id != program_id) {
            LOG_WARNING(Service_BOSS,
                        "Mismatched program ID in SpotPass data. Was expecting "
                        "{:#018x}, found {:#018x}",
                        program_id, static_cast<u64>(entry.header.program_id));
            continue;
        }

        // Check the payload size is correct, excluding header
        if (entry.header.payload_size != (current_file.file_size - boss_header_length)) {
            LOG_WARNING(Service_BOSS,
                        "Mismatched file size, was expecting {:#010x}, found {:#010x}",
                        static_cast<u32>(entry.header.payload_size),
                        current_file.file_size - boss_header_length);
            continue;
        }

        ns_data.push_back(entry);
    }

    return ns_data;
}

u16 OnlineService::GetNsDataIdList(const u32 filter, const u32 max_entries,
                                   Kernel::MappedBuffer& buffer) {
    std::vector<NsDataEntry> ns_data = GetNsDataEntries();
    std::vector<u32> output_entries;
    for (const auto& current_entry : ns_data) {
        const u32 datatype_raw = static_cast<u32>(current_entry.header.datatype);
        const u16 datatype_high = static_cast<u16>(datatype_raw >> 16);
        const u16 datatype_low = static_cast<u16>(datatype_raw & 0xFFFF);
        const u16 filter_high = static_cast<u16>(filter >> 16);
        const u16 filter_low = static_cast<u16>(filter & 0xFFFF);
        if (filter != 0xFFFFFFFF &&
            (filter_high != datatype_high || (filter_low & datatype_low) == 0)) {
            continue;
        }

        if (output_entries.size() >= max_entries) {
            LOG_WARNING(Service_BOSS, "Reached maximum number of entries");
            break;
        }

        output_entries.push_back(current_entry.header.ns_data_id);
    }

    buffer.Write(output_entries.data(), 0, sizeof(u32) * output_entries.size());
    return static_cast<u16>(output_entries.size());
}

std::optional<NsDataEntry> OnlineService::GetNsDataEntryFromId(const u32 ns_data_id) {
    std::vector<NsDataEntry> ns_data = GetNsDataEntries();
    const auto entry_iter = std::find_if(ns_data.begin(), ns_data.end(), [ns_data_id](auto entry) {
        return entry.header.ns_data_id == ns_data_id;
    });
    if (entry_iter == ns_data.end()) {
        LOG_WARNING(Service_BOSS, "Could not find NsData with ID {:#010X}", ns_data_id);
        return std::nullopt;
    }
    return *entry_iter;
}

Result OnlineService::GetNsDataHeaderInfo(const u32 ns_data_id, const NsDataHeaderInfoType type,
                                          const u32 size, Kernel::MappedBuffer& buffer) {
    const auto entry = GetNsDataEntryFromId(ns_data_id);
    if (!entry.has_value()) {
        LOG_WARNING(Service_BOSS, "Failed to find NsData entry for ID {:#010X}", ns_data_id);
        return {ErrCodes::NsDataNotFound, ErrorModule::BOSS, ErrorSummary::InvalidState,
                ErrorLevel::Status};
    }

    static constexpr std::array EXPECTED_NS_DATA_HEADER_INFO_SIZES = {
        sizeof(u64),              // Program ID
        sizeof(u32),              // Unknown
        sizeof(u32),              // Data Type
        sizeof(u32),              // Payload Size
        sizeof(u32),              // NsData ID
        sizeof(u32),              // Version
        sizeof(NsDataHeaderInfo), // Everything
    };
    if (size != EXPECTED_NS_DATA_HEADER_INFO_SIZES[static_cast<u8>(type)]) {
        LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
        // TODO: Proper error code.
        return ResultUnknown;
    }

    switch (type) {
    case NsDataHeaderInfoType::ProgramId:
        buffer.Write(&entry->header.program_id, 0, size);
        return ResultSuccess;
    case NsDataHeaderInfoType::Unknown: {
        // TODO: Figure out what this is. Stubbed to zero for now.
        const u32 zero = 0;
        buffer.Write(&zero, 0, size);
        return ResultSuccess;
    }
    case NsDataHeaderInfoType::Datatype:
        buffer.Write(&entry->header.datatype, 0, size);
        return ResultSuccess;
    case NsDataHeaderInfoType::PayloadSize:
        buffer.Write(&entry->header.payload_size, 0, size);
        return ResultSuccess;
    case NsDataHeaderInfoType::NsDataId:
        buffer.Write(&entry->header.ns_data_id, 0, size);
        return ResultSuccess;
    case NsDataHeaderInfoType::Version:
        buffer.Write(&entry->header.version, 0, size);
        return ResultSuccess;
    case NsDataHeaderInfoType::Everything: {
        const NsDataHeaderInfo info = {
            .program_id = entry->header.program_id,
            .datatype = entry->header.datatype,
            .payload_size = entry->header.payload_size,
            .ns_data_id = entry->header.ns_data_id,
            .version = entry->header.version,
        };
        buffer.Write(&info, 0, size);
        return ResultSuccess;
    }
    default:
        LOG_WARNING(Service_BOSS, "Unknown header info type {}", type);
        // TODO: Proper error code.
        return ResultUnknown;
    }
}

ResultVal<std::size_t> OnlineService::ReadNsData(const u32 ns_data_id, const u64 offset,
                                                 const u32 size, Kernel::MappedBuffer& buffer) {
    std::optional<NsDataEntry> entry = GetNsDataEntryFromId(ns_data_id);
    if (!entry.has_value()) {
        LOG_WARNING(Service_BOSS, "Failed to find NsData entry for ID {:#010X}", ns_data_id);
        return Result(ErrCodes::NsDataNotFound, ErrorModule::BOSS, ErrorSummary::InvalidState,
                      ErrorLevel::Status);
    }

    if (entry->header.payload_size < size + offset) {
        LOG_WARNING(Service_BOSS,
                    "Invalid request to read {:#010X} bytes at offset {:#010X}, payload "
                    "length is {:#010X}",
                    size, offset, static_cast<u32>(entry->header.payload_size));
        // TODO: Proper error code.
        return ResultUnknown;
    }

    auto boss_archive = OpenBossExtData();
    if (!boss_archive) {
        // TODO: Proper error code.
        return ResultUnknown;
    }

    FileSys::Path file_path = fmt::format("/{}", entry->filename);
    FileSys::Mode mode{};
    mode.read_flag.Assign(1);
    auto file_result = boss_archive->OpenFile(file_path, mode);
    if (!file_result.Succeeded()) {
        LOG_WARNING(Service_BOSS, "Failed to open SpotPass extdata file '{}'.", entry->filename);
        // TODO: Proper error code.
        return ResultUnknown;
    }

    auto file = std::move(file_result).Unwrap();
    std::vector<u8> ns_data_array(size);
    auto read_result = file->Read(sizeof(BossHeader) + offset, size, ns_data_array.data());
    if (!read_result.Succeeded()) {
        LOG_WARNING(Service_BOSS, "Failed to read SpotPass extdata file '{}'.", entry->filename);
        // TODO: Proper error code.
        return ResultUnknown;
    }

    buffer.Write(ns_data_array.data(), 0, size);
    return read_result;
}

template <class... Ts>
struct overload : Ts... {
    using Ts::operator()...;
};
template <class... Ts>
overload(Ts...) -> overload<Ts...>;

Result OnlineService::SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) {
    const auto property_id = static_cast<PropertyID>(id);
    if (!current_props.properties.contains(property_id)) {
        LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
        return Result(ErrCodes::UnknownPropertyID, ErrorModule::BOSS, ErrorSummary::Internal,
                      ErrorLevel::Status);
    }

    auto& prop = current_props.properties[property_id];
    auto read_pod = [&]<typename T>(T& old_prop) {
        static_assert(std::is_trivial<T>::value, "Only trivial types are allowed for read_pod");
        if (size != sizeof(old_prop)) {
            LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
                      property_id, sizeof(old_prop), size);
        }
        T cur_prop = 0;
        buffer.Read(&cur_prop, 0, size);
        prop = cur_prop;
    };

    auto read_vector = [&]<typename T>(std::vector<T>& old_prop) {
        if (size != old_prop.size()) {
            LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
                      property_id, old_prop.size(), size);
        }
        std::vector<T> cur_prop(size);
        buffer.Read(cur_prop.data(), 0, size);
        prop = cur_prop;
    };

    std::visit(overload{[&](u8& cur_prop) { read_pod(cur_prop); },
                        [&](u16& cur_prop) { read_pod(cur_prop); },
                        [&](u32& cur_prop) { read_pod(cur_prop); },
                        [&](u64& cur_prop) { read_pod(cur_prop); },
                        [&](std::vector<u8>& cur_prop) { read_vector(cur_prop); },
                        [&](std::vector<u32>& cur_prop) { read_vector(cur_prop); }},
               prop);

    return ResultSuccess;
}

Result OnlineService::ReceiveProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) {
    const auto property_id = static_cast<PropertyID>(id);
    if (!current_props.properties.contains(property_id)) {
        LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
        return {ErrCodes::UnknownPropertyID, ErrorModule::BOSS, ErrorSummary::Internal,
                ErrorLevel::Status};
    }

    auto write_pod = [&]<typename T>(T& cur_prop) {
        static_assert(std::is_trivial<T>::value, "Only trivial types are allowed for write_pod");
        if (size != sizeof(cur_prop)) {
            LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
                      property_id, sizeof(cur_prop), size);
        }
        buffer.Write(&cur_prop, 0, size);
    };

    auto write_vector = [&]<typename T>(std::vector<T>& cur_prop) {
        if (size != cur_prop.size()) {
            LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}",
                      property_id, cur_prop.size(), size);
        }
        buffer.Write(cur_prop.data(), 0, size);
    };

    auto& prop = current_props.properties[property_id];
    std::visit(overload{[&](u8& cur_prop) { write_pod(cur_prop); },
                        [&](u16& cur_prop) { write_pod(cur_prop); },
                        [&](u32& cur_prop) { write_pod(cur_prop); },
                        [&](u64& cur_prop) { write_pod(cur_prop); },
                        [&](std::vector<u8>& cur_prop) { write_vector(cur_prop); },
                        [&](std::vector<u32>& cur_prop) { write_vector(cur_prop); }},
               prop);

    return ResultSuccess;
}

} // namespace Service::BOSS