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
|
#include "KingSystem/Resource/resResourceGameSaveData.h"
#include <algorithm>
#include <codec/seadHashCRC32.h>
#include <prim/seadContainerIterator.h>
#include "KingSystem/GameData/gdtManager.h"
#include "KingSystem/Utils/Byaml/Byaml.h"
namespace ksys::res {
void GameSaveData::File::forEachFlag(const sead::Delegate1<SaveMgr, const Flag&>& delegate) {
for (const auto& flag : flags)
delegate(flag);
}
void GameSaveData::File::forEachFlag(const sead::Delegate1<SaveMgr, const Flag&>& delegate,
s32 start, s32 end) {
if (start == end)
return;
for (auto it = flags.begin(start), it_end = flags.begin(end); it != it_end; ++it)
delegate(*it);
}
void GameSaveData::File::forEachFlag(const sead::Delegate1<SaveMgr, Flag&>& delegate) {
for (auto& flag : flags)
delegate(flag);
}
s32 GameSaveData::File::findFlagIndex(u32 flag_name_hash) const {
s32 a = 0;
s32 b = flags.size() - 1;
while (a < b) {
const s32 m = (a + b) / 2;
if (u32(flags[m].kv.name_hash) == flag_name_hash)
return m;
if (u32(flags[m].kv.name_hash) < flag_name_hash)
a = m + 1;
else
b = m;
}
if (u32(flags[a].kv.name_hash) == flag_name_hash)
return a;
return -1;
}
// NON_MATCHING: cNullChar is loaded too late (which throws off a lot of things) and Clang is
// using a different register to access file->info
void GameSaveData::doCreate_(u8* buffer, u32, sead::Heap*) {
auto* heap = gdt::Manager::instance()->getSaveAreaHeap();
al::ByamlIter root_iter{buffer};
al::ByamlIter file_info_iter;
al::ByamlIter save_info_iter;
al::ByamlIter flags_iter;
{
al::ByamlIter iter;
if (root_iter.tryGetIterByKey(&iter, "file_list")) {
iter.tryGetIterByIndex(&file_info_iter, 0);
iter.tryGetIterByIndex(&flags_iter, 1);
}
al::ByamlIter iter2;
if (root_iter.tryGetIterByKey(&iter2, "save_info"))
iter2.tryGetIterByIndex(&save_info_iter, 0);
}
mFiles.allocBuffer(1, heap);
mSaveInfo = new (heap) SaveInfo;
save_info_iter.tryGetIntByKey(&mSaveInfo->revision, "revision");
save_info_iter.tryGetIntByKey(&mSaveInfo->directory_num, "directory_num");
save_info_iter.tryGetBoolByKey(&mSaveInfo->is_build_machine, "is_build_machine");
#ifdef MATCHING_HACK_NX_CLANG
// This isn't required but makes the diff cleaner... cNullChar is loaded here in the original.
*static_cast<volatile const char*>(&sead::SafeString::cNullChar);
#endif
const auto num_flags = flags_iter.getSize();
auto* file = new (heap) File;
file->name_hash = std::numeric_limits<int>::min();
file->info = new (heap) FileInfo;
const char* file_name = "";
file_info_iter.tryGetStringByKey(&file_name, "file_name");
auto* file_name_str = new (heap) sead::FixedSafeString<64>;
file_name_str->copy(file_name);
file->info->name = file_name_str->cstr();
file_info_iter.tryGetBoolByKey(&file->info->is_common, "IsCommon");
file_info_iter.tryGetBoolByKey(&file->info->is_common_at_same_account, "IsCommonAtSameAccount");
file_info_iter.tryGetBoolByKey(&file->info->is_save_secure_code, "IsSaveSecureCode");
if (num_flags > 0) {
file->flags.allocBuffer(num_flags, heap);
for (s32 i = 0; i < num_flags; ++i) {
al::ByamlIter iter;
if (!flags_iter.tryGetIterByIndex(&iter, i))
continue;
Flag flag;
iter.tryGetIntByKey(&flag.kv.name_hash, "HashValue");
const char* data_name = "";
iter.tryGetStringByKey(&data_name, "DataName");
const s32 data_name_hash = sead::HashCRC32::calcStringHash(data_name);
if (flag.kv.name_hash != data_name_hash)
flag.kv.name_hash = data_name_hash;
file->flags.pushBack(flag);
}
}
mFiles.pushBack(file);
}
void GameSaveData::finalize() {
if (mSaveInfo)
delete mSaveInfo;
if (mFiles.isBufferReady()) {
for (auto it = mFiles.begin(), end = mFiles.end(); it != end; ++it) {
it->flags.freeBuffer();
delete &*it;
[[maybe_unused]] auto next = it;
++next;
}
mFiles.freeBuffer();
}
}
void GameSaveData::allocFiles(s32 count, sead::Heap* heap) {
mFiles.allocBuffer(count, heap);
}
void GameSaveData::addFile(u32 hash, sead::Heap* heap) {
if (!mFiles.isBufferReady())
return;
auto* file = new (heap) File;
file->info = nullptr;
file->name_hash = hash;
mFiles.pushBack(file);
}
std::optional<GameSaveData::File*> GameSaveData::getFile(u32 hash) const {
for (auto& file : mFiles)
if (file.name_hash == hash)
return &file;
return std::nullopt;
}
void GameSaveData::allocFlags(u32 file_name_hash, s32 count, sead::Heap* heap) {
if (!mFiles.isBufferReady())
return;
auto file = getFile(file_name_hash);
if (!file)
return;
(*file)->flags.allocBuffer(count, heap);
}
void GameSaveData::copyFrom(const GameSaveData& src, sead::Heap* heap) {
if (!mSaveInfo && src.mSaveInfo) {
mSaveInfo = new (heap) SaveInfo;
*mSaveInfo = *src.mSaveInfo;
}
if (!mFiles.isBufferReady())
return;
auto it = mFiles.begin(), end = mFiles.end();
const File& src_file = *src.mFiles[0];
for (; it != end; ++it) {
const u32 other_hash = sead::HashCRC32::calcStringHash(src_file.info->name);
if (it->name_hash != other_hash)
continue;
if (it->info == nullptr) {
it->info = new (heap) FileInfo;
auto* target = it->info;
*target = *src_file.info;
}
for (auto flag = src_file.flags.begin(), e = src_file.flags.end(); flag != e; ++flag)
it->flags.pushBack(*flag);
}
}
[[gnu::noinline]] static void sortGameSaveDataFlags(sead::RingBuffer<GameSaveData::Flag>& flags) {
const auto wrapper = sead::stdIterator(flags);
std::sort(wrapper.begin(), wrapper.end(),
[](const GameSaveData::Flag& lhs, const GameSaveData::Flag& rhs) {
return lhs.kv.name_hash < rhs.kv.name_hash;
});
}
void GameSaveData::sortFlags() {
if (!mFiles.isBufferReady())
return;
for (auto& file : mFiles)
sortGameSaveDataFlags(file.flags);
}
} // namespace ksys::res
|