summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys/vfs_concat.cpp
blob: d23623aa04fa20370673c24eafd0d3fa0758cff9 (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
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <utility>

#include "common/assert.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_static.h"

namespace FileSys {

static bool VerifyConcatenationMapContinuity(const std::multimap<u64, VirtualFile>& map) {
    const auto last_valid = --map.end();
    for (auto iter = map.begin(); iter != last_valid;) {
        const auto old = iter++;
        if (old->first + old->second->GetSize() != iter->first) {
            return false;
        }
    }

    return map.begin()->first == 0;
}

ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name_)
    : name(std::move(name_)) {
    std::size_t next_offset = 0;
    for (const auto& file : files_) {
        files.emplace(next_offset, file);
        next_offset += file->GetSize();
    }
}

ConcatenatedVfsFile::ConcatenatedVfsFile(std::multimap<u64, VirtualFile> files_, std::string name_)
    : files(std::move(files_)), name(std::move(name_)) {
    ASSERT(VerifyConcatenationMapContinuity(files));
}

ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;

VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files,
                                                      std::string name) {
    if (files.empty())
        return nullptr;
    if (files.size() == 1)
        return files[0];

    return VirtualFile(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}

VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
                                                      std::multimap<u64, VirtualFile> files,
                                                      std::string name) {
    if (files.empty())
        return nullptr;
    if (files.size() == 1)
        return files.begin()->second;

    const auto last_valid = --files.end();
    for (auto iter = files.begin(); iter != last_valid;) {
        const auto old = iter++;
        if (old->first + old->second->GetSize() != iter->first) {
            files.emplace(old->first + old->second->GetSize(),
                          std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first -
                                                                           old->second->GetSize()));
        }
    }

    // Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
    if (files.begin()->first != 0)
        files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first));

    return VirtualFile(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}

std::string ConcatenatedVfsFile::GetName() const {
    if (files.empty()) {
        return "";
    }
    if (!name.empty()) {
        return name;
    }
    return files.begin()->second->GetName();
}

std::size_t ConcatenatedVfsFile::GetSize() const {
    if (files.empty()) {
        return 0;
    }
    return files.rbegin()->first + files.rbegin()->second->GetSize();
}

bool ConcatenatedVfsFile::Resize(std::size_t new_size) {
    return false;
}

VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const {
    if (files.empty()) {
        return nullptr;
    }
    return files.begin()->second->GetContainingDirectory();
}

bool ConcatenatedVfsFile::IsWritable() const {
    return false;
}

bool ConcatenatedVfsFile::IsReadable() const {
    return true;
}

std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
    auto entry = --files.end();
    for (auto iter = files.begin(); iter != files.end(); ++iter) {
        if (iter->first > offset) {
            entry = --iter;
            break;
        }
    }

    if (entry->first + entry->second->GetSize() <= offset)
        return 0;

    const auto read_in =
        std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize());
    if (length > read_in) {
        return entry->second->Read(data, read_in, offset - entry->first) +
               Read(data + read_in, length - read_in, offset + read_in);
    }

    return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first);
}

std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
    return 0;
}

bool ConcatenatedVfsFile::Rename(std::string_view new_name) {
    return false;
}

} // namespace FileSys