Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/io/file_access_patched.cpp
20886 views
1
/**************************************************************************/
2
/* file_access_patched.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "file_access_patched.h"
32
33
#include "file_access_pack.h"
34
35
#include "core/io/delta_encoding.h"
36
#include "core/os/os.h"
37
38
Error FileAccessPatched::_apply_patch() const {
39
ERR_FAIL_COND_V(!is_open(), FAILED);
40
41
String path = old_file->get_path();
42
Vector<PackedData::PackedFile> delta_patches = PackedData::get_singleton()->get_delta_patches(path);
43
Vector<uint8_t> old_file_data = old_file->get_buffer(old_file->get_length());
44
45
for (int i = 0; i < delta_patches.size(); ++i) {
46
const PackedData::PackedFile &delta_patch = delta_patches[i];
47
ERR_FAIL_COND_V(delta_patch.bundle, FAILED);
48
49
Error err = OK;
50
51
uint64_t total_usec_start = OS::get_singleton()->get_ticks_usec();
52
uint64_t io_usec_start = OS::get_singleton()->get_ticks_usec();
53
54
Ref<FileAccess> patch_file = FileAccess::open(delta_patch.pack, FileAccess::READ, &err);
55
ERR_FAIL_COND_V(err != OK, err);
56
57
patch_file->seek(delta_patch.offset);
58
ERR_FAIL_COND_V(patch_file->get_error() != OK, patch_file->get_error());
59
60
Vector<uint8_t> patch_data = patch_file->get_buffer(delta_patch.size);
61
ERR_FAIL_COND_V(patch_data.is_empty(), ERR_FILE_CANT_READ);
62
63
uint64_t io_usec_end = OS::get_singleton()->get_ticks_usec();
64
uint64_t decode_usec_start = OS::get_singleton()->get_ticks_usec();
65
66
Vector<uint8_t> new_file_data;
67
err = DeltaEncoding::decode_delta(old_file_data, patch_data, new_file_data);
68
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to apply delta patch (%d of %d) to \"%s\".", i + 1, delta_patches.size(), path));
69
70
uint64_t decode_usec_end = OS::get_singleton()->get_ticks_usec();
71
72
old_file_data = new_file_data;
73
74
uint64_t total_usec_end = OS::get_singleton()->get_ticks_usec();
75
76
print_verbose(vformat(U"Applied delta patch to \"%s\" from \"%s\" in %d μs (%d μs I/O, %d μs decoding).", path, delta_patch.pack.get_file(), total_usec_end - total_usec_start, io_usec_end - io_usec_start, decode_usec_end - decode_usec_start));
77
}
78
79
patched_file_data = old_file_data;
80
patched_file.instantiate();
81
return patched_file->open_custom(patched_file_data.ptr(), patched_file_data.size());
82
}
83
84
bool FileAccessPatched::_try_apply_patch() const {
85
if (last_error != OK) {
86
return false;
87
}
88
89
if (patched_file.is_valid()) {
90
return true;
91
}
92
93
last_error = _apply_patch();
94
return last_error == OK;
95
}
96
97
Error FileAccessPatched::open_custom(const Ref<FileAccess> &p_old_file) {
98
close();
99
100
if (!p_old_file->is_open()) {
101
last_error = ERR_FILE_CANT_OPEN;
102
return last_error;
103
}
104
105
old_file = p_old_file;
106
107
return OK;
108
}
109
110
bool FileAccessPatched::is_open() const {
111
return old_file.is_valid() && old_file->is_open();
112
}
113
114
void FileAccessPatched::seek(uint64_t p_position) {
115
if (!_try_apply_patch()) {
116
return;
117
}
118
119
patched_file->seek(p_position);
120
}
121
122
void FileAccessPatched::seek_end(int64_t p_position) {
123
if (!_try_apply_patch()) {
124
return;
125
}
126
127
patched_file->seek_end(p_position);
128
}
129
130
uint64_t FileAccessPatched::get_position() const {
131
if (!_try_apply_patch()) {
132
return 0;
133
}
134
135
return patched_file->get_position();
136
}
137
138
uint64_t FileAccessPatched::get_length() const {
139
if (!_try_apply_patch()) {
140
return 0;
141
}
142
143
return patched_file->get_length();
144
}
145
146
bool FileAccessPatched::eof_reached() const {
147
if (!_try_apply_patch()) {
148
return true;
149
}
150
151
return patched_file->eof_reached();
152
}
153
154
Error FileAccessPatched::get_error() const {
155
if (last_error != OK) {
156
return last_error;
157
}
158
159
if (patched_file.is_valid()) {
160
Error inner_error = patched_file->get_error();
161
if (inner_error != OK) {
162
return inner_error;
163
}
164
}
165
166
return last_error;
167
}
168
169
bool FileAccessPatched::store_buffer(const uint8_t *p_src, uint64_t p_length) {
170
if (!_try_apply_patch()) {
171
return false;
172
}
173
174
return patched_file->store_buffer(p_src, p_length);
175
}
176
177
uint64_t FileAccessPatched::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
178
if (!_try_apply_patch()) {
179
return 0;
180
}
181
182
return patched_file->get_buffer(p_dst, p_length);
183
}
184
185
void FileAccessPatched::flush() {
186
if (!_try_apply_patch()) {
187
return;
188
}
189
190
patched_file->flush();
191
}
192
193
void FileAccessPatched::close() {
194
old_file = Ref<FileAccess>();
195
patched_file = Ref<FileAccessMemory>();
196
patched_file_data.clear();
197
last_error = OK;
198
}
199
200
bool FileAccessPatched::file_exists(const String &p_name) {
201
ERR_FAIL_COND_V(old_file.is_null(), false);
202
return old_file->file_exists(p_name);
203
}
204
205