Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/io/file_access.cpp
9973 views
1
/**************************************************************************/
2
/* file_access.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.h"
32
#include "file_access.compat.inc"
33
34
#include "core/config/project_settings.h"
35
#include "core/crypto/crypto_core.h"
36
#include "core/io/file_access_compressed.h"
37
#include "core/io/file_access_encrypted.h"
38
#include "core/io/file_access_pack.h"
39
#include "core/io/marshalls.h"
40
#include "core/os/os.h"
41
#include "core/os/time.h"
42
43
Ref<FileAccess> FileAccess::create(AccessType p_access) {
44
ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
45
ERR_FAIL_NULL_V(create_func[p_access], nullptr);
46
47
Ref<FileAccess> ret = create_func[p_access]();
48
ret->_set_access_type(p_access);
49
return ret;
50
}
51
52
bool FileAccess::exists(const String &p_name) {
53
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_name)) {
54
return true;
55
}
56
57
// Using file_exists because it's faster than trying to open the file.
58
Ref<FileAccess> ret = create_for_path(p_name);
59
return ret->file_exists(p_name);
60
}
61
62
void FileAccess::_set_access_type(AccessType p_access) {
63
_access_type = p_access;
64
}
65
66
Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
67
Ref<FileAccess> ret;
68
if (p_path.begins_with("res://") || p_path.begins_with("uid://")) {
69
ret = create(ACCESS_RESOURCES);
70
} else if (p_path.begins_with("user://")) {
71
ret = create(ACCESS_USERDATA);
72
} else if (p_path.begins_with("pipe://")) {
73
ret = create(ACCESS_PIPE);
74
} else {
75
ret = create(ACCESS_FILESYSTEM);
76
}
77
78
return ret;
79
}
80
81
Ref<FileAccess> FileAccess::create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep, Error *r_error) {
82
const String ERROR_COMMON_PREFIX = "Error while creating temporary file";
83
84
if (!p_prefix.is_valid_filename()) {
85
*r_error = ERR_FILE_BAD_PATH;
86
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix));
87
}
88
89
if (!p_extension.is_valid_filename()) {
90
*r_error = ERR_FILE_BAD_PATH;
91
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid extension.)", ERROR_COMMON_PREFIX, p_extension));
92
}
93
94
const String TEMP_DIR = OS::get_singleton()->get_temp_path();
95
String extension = p_extension.trim_prefix(".");
96
97
uint32_t suffix_i = 0;
98
String path;
99
while (true) {
100
String datetime = Time::get_singleton()->get_datetime_string_from_system().remove_chars("-T:");
101
datetime += itos(Time::get_singleton()->get_ticks_usec());
102
String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : "");
103
path = TEMP_DIR.path_join((p_prefix.is_empty() ? "" : p_prefix + "-") + suffix + (extension.is_empty() ? "" : "." + extension));
104
if (!DirAccess::exists(path)) {
105
break;
106
}
107
suffix_i += 1;
108
}
109
110
Error err;
111
{
112
// Create file first with WRITE mode.
113
// Otherwise, it would fail to open with a READ mode.
114
Ref<FileAccess> ret = FileAccess::open(path, FileAccess::ModeFlags::WRITE, &err);
115
if (err != OK) {
116
*r_error = err;
117
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: could not create "%s".)", ERROR_COMMON_PREFIX, path));
118
}
119
ret->flush();
120
}
121
122
// Open then the temp file with the correct mode flag.
123
Ref<FileAccess> ret = FileAccess::open(path, p_mode_flags, &err);
124
if (err != OK) {
125
*r_error = err;
126
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: could not open "%s".)", ERROR_COMMON_PREFIX, path));
127
}
128
if (ret.is_valid()) {
129
ret->_is_temp_file = true;
130
ret->_temp_keep_after_use = p_keep;
131
ret->_temp_path = ret->get_path_absolute();
132
}
133
134
*r_error = OK;
135
return ret;
136
}
137
138
Ref<FileAccess> FileAccess::_create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep) {
139
return create_temp(p_mode_flags, p_prefix, p_extension, p_keep, &last_file_open_error);
140
}
141
142
void FileAccess::_delete_temp() {
143
if (!_is_temp_file || _temp_keep_after_use) {
144
return;
145
}
146
147
if (!FileAccess::exists(_temp_path)) {
148
return;
149
}
150
151
DirAccess::remove_absolute(_temp_path);
152
}
153
154
Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
155
return open_internal(p_path, p_mode_flags);
156
}
157
158
Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) {
159
//try packed data first
160
161
Ref<FileAccess> ret;
162
if (!(p_mode_flags & WRITE) && !(p_mode_flags & SKIP_PACK) && PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled()) {
163
ret = PackedData::get_singleton()->try_open_path(p_path);
164
if (ret.is_valid()) {
165
if (r_error) {
166
*r_error = OK;
167
}
168
return ret;
169
}
170
}
171
172
ret = create_for_path(p_path);
173
Error err = ret->open_internal(p_path, p_mode_flags & ~SKIP_PACK);
174
175
if (r_error) {
176
*r_error = err;
177
}
178
if (err != OK) {
179
ret.unref();
180
}
181
182
return ret;
183
}
184
185
Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags) {
186
Error err = OK;
187
Ref<FileAccess> fa = open(p_path, p_mode_flags, &err);
188
last_file_open_error = err;
189
if (err) {
190
return Ref<FileAccess>();
191
}
192
return fa;
193
}
194
195
Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv) {
196
Ref<FileAccess> fa = _open(p_path, p_mode_flags);
197
if (fa.is_null()) {
198
return fa;
199
}
200
201
Ref<FileAccessEncrypted> fae;
202
fae.instantiate();
203
Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ, true, p_iv);
204
last_file_open_error = err;
205
if (err) {
206
return Ref<FileAccess>();
207
}
208
return fae;
209
}
210
211
Ref<FileAccess> FileAccess::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) {
212
Ref<FileAccess> fa = _open(p_path, p_mode_flags);
213
if (fa.is_null()) {
214
return fa;
215
}
216
217
Ref<FileAccessEncrypted> fae;
218
fae.instantiate();
219
Error err = fae->open_and_parse_password(fa, p_pass, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ);
220
last_file_open_error = err;
221
if (err) {
222
return Ref<FileAccess>();
223
}
224
return fae;
225
}
226
227
Ref<FileAccess> FileAccess::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) {
228
Ref<FileAccessCompressed> fac;
229
fac.instantiate();
230
fac->configure("GCPF", (Compression::Mode)p_compress_mode);
231
Error err = fac->open_internal(p_path, p_mode_flags);
232
last_file_open_error = err;
233
if (err) {
234
return Ref<FileAccess>();
235
}
236
237
return fac;
238
}
239
240
Error FileAccess::get_open_error() {
241
return last_file_open_error;
242
}
243
244
FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) {
245
return create_func[p_access];
246
}
247
248
FileAccess::AccessType FileAccess::get_access_type() const {
249
return _access_type;
250
}
251
252
String FileAccess::fix_path(const String &p_path) const {
253
// Helper used by file accesses that use a single filesystem.
254
255
String r_path = p_path.replace_char('\\', '/');
256
257
switch (_access_type) {
258
case ACCESS_RESOURCES: {
259
if (ProjectSettings::get_singleton()) {
260
if (r_path.begins_with("uid://")) {
261
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(r_path);
262
if (ResourceUID::get_singleton()->has_id(uid)) {
263
r_path = ResourceUID::get_singleton()->get_id_path(uid);
264
} else {
265
r_path.clear();
266
}
267
}
268
269
if (r_path.begins_with("res://")) {
270
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
271
if (!resource_path.is_empty()) {
272
return r_path.replace("res:/", resource_path);
273
}
274
return r_path.replace("res://", "");
275
}
276
}
277
278
} break;
279
case ACCESS_USERDATA: {
280
if (r_path.begins_with("user://")) {
281
String data_dir = OS::get_singleton()->get_user_data_dir();
282
if (!data_dir.is_empty()) {
283
return r_path.replace("user:/", data_dir);
284
}
285
return r_path.replace("user://", "");
286
}
287
288
} break;
289
case ACCESS_PIPE: {
290
return r_path;
291
} break;
292
case ACCESS_FILESYSTEM: {
293
return r_path;
294
} break;
295
case ACCESS_MAX:
296
break; // Can't happen, but silences warning
297
}
298
299
return r_path;
300
}
301
302
/* these are all implemented for ease of porting, then can later be optimized */
303
uint8_t FileAccess::get_8() const {
304
uint8_t data = 0;
305
get_buffer(&data, sizeof(uint8_t));
306
307
return data;
308
}
309
310
uint16_t FileAccess::get_16() const {
311
uint16_t data = 0;
312
get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint16_t));
313
314
#ifdef BIG_ENDIAN_ENABLED
315
if (!big_endian) {
316
data = BSWAP16(data);
317
}
318
#else
319
if (big_endian) {
320
data = BSWAP16(data);
321
}
322
#endif
323
324
return data;
325
}
326
327
uint32_t FileAccess::get_32() const {
328
uint32_t data = 0;
329
get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint32_t));
330
331
#ifdef BIG_ENDIAN_ENABLED
332
if (!big_endian) {
333
data = BSWAP32(data);
334
}
335
#else
336
if (big_endian) {
337
data = BSWAP32(data);
338
}
339
#endif
340
341
return data;
342
}
343
344
uint64_t FileAccess::get_64() const {
345
uint64_t data = 0;
346
get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint64_t));
347
348
#ifdef BIG_ENDIAN_ENABLED
349
if (!big_endian) {
350
data = BSWAP64(data);
351
}
352
#else
353
if (big_endian) {
354
data = BSWAP64(data);
355
}
356
#endif
357
358
return data;
359
}
360
361
float FileAccess::get_half() const {
362
return Math::half_to_float(get_16());
363
}
364
365
float FileAccess::get_float() const {
366
MarshallFloat m;
367
m.i = get_32();
368
return m.f;
369
}
370
371
real_t FileAccess::get_real() const {
372
if (real_is_double) {
373
return get_double();
374
} else {
375
return get_float();
376
}
377
}
378
379
Variant FileAccess::get_var(bool p_allow_objects) const {
380
uint32_t len = get_32();
381
Vector<uint8_t> buff = get_buffer(len);
382
ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant());
383
384
const uint8_t *r = buff.ptr();
385
386
Variant v;
387
Error err = decode_variant(v, &r[0], len, nullptr, p_allow_objects);
388
ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to encode Variant.");
389
390
return v;
391
}
392
393
double FileAccess::get_double() const {
394
MarshallDouble m;
395
m.l = get_64();
396
return m.d;
397
}
398
399
String FileAccess::get_token() const {
400
CharString token;
401
402
uint8_t c = get_8();
403
404
while (!eof_reached()) {
405
if (c <= ' ') {
406
if (token.length()) {
407
break;
408
}
409
} else {
410
token += char(c);
411
}
412
c = get_8();
413
}
414
415
return String::utf8(token.get_data());
416
}
417
418
class CharBuffer {
419
Vector<char> vector;
420
char stack_buffer[256];
421
422
char *buffer = nullptr;
423
int64_t capacity = 0;
424
int64_t written = 0;
425
426
bool grow() {
427
if (vector.resize(next_power_of_2((uint64_t)1 + (uint64_t)written)) != OK) {
428
return false;
429
}
430
431
if (buffer == stack_buffer) { // first chunk?
432
433
for (int64_t i = 0; i < written; i++) {
434
vector.write[i] = stack_buffer[i];
435
}
436
}
437
438
buffer = vector.ptrw();
439
capacity = vector.size();
440
ERR_FAIL_COND_V(written >= capacity, false);
441
442
return true;
443
}
444
445
public:
446
_FORCE_INLINE_ CharBuffer() :
447
buffer(stack_buffer),
448
capacity(std::size(stack_buffer)) {
449
}
450
451
_FORCE_INLINE_ void push_back(char c) {
452
if (written >= capacity) {
453
ERR_FAIL_COND(!grow());
454
}
455
456
buffer[written++] = c;
457
}
458
459
_FORCE_INLINE_ const char *get_data() const {
460
return buffer;
461
}
462
};
463
464
String FileAccess::get_line() const {
465
CharBuffer line;
466
467
uint8_t c = get_8();
468
469
while (!eof_reached()) {
470
if (c == '\n' || c == '\0' || get_error() != OK) {
471
line.push_back(0);
472
return String::utf8(line.get_data());
473
} else if (c != '\r') {
474
line.push_back(char(c));
475
}
476
477
c = get_8();
478
}
479
line.push_back(0);
480
return String::utf8(line.get_data());
481
}
482
483
Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
484
ERR_FAIL_COND_V_MSG(p_delim.length() != 1, Vector<String>(), "Only single character delimiters are supported to parse CSV lines.");
485
ERR_FAIL_COND_V_MSG(p_delim[0] == '"', Vector<String>(), "The double quotation mark character (\") is not supported as a delimiter for CSV lines.");
486
487
String line;
488
489
// CSV can support entries with line breaks as long as they are enclosed
490
// in double quotes. So our "line" might be more than a single line in the
491
// text file.
492
int qc = 0;
493
do {
494
if (eof_reached()) {
495
break;
496
}
497
line += get_line() + "\n";
498
qc = 0;
499
for (int i = 0; i < line.length(); i++) {
500
if (line[i] == '"') {
501
qc++;
502
}
503
}
504
} while (qc % 2);
505
506
// Remove the extraneous newline we've added above.
507
line = line.substr(0, line.length() - 1);
508
509
Vector<String> strings;
510
511
bool in_quote = false;
512
String current;
513
for (int i = 0; i < line.length(); i++) {
514
char32_t c = line[i];
515
// A delimiter ends the current entry, unless it's in a quoted string.
516
if (!in_quote && c == p_delim[0]) {
517
strings.push_back(current);
518
current = String();
519
} else if (c == '"') {
520
// Doubled quotes are escapes for intentional quotes in the string.
521
if (line[i + 1] == '"' && in_quote) {
522
current += '"';
523
i++;
524
} else {
525
in_quote = !in_quote;
526
}
527
} else {
528
current += c;
529
}
530
}
531
532
if (in_quote) {
533
WARN_PRINT(vformat("Reached end of file before closing '\"' in CSV file '%s'.", get_path()));
534
}
535
536
strings.push_back(current);
537
538
return strings;
539
}
540
541
String FileAccess::get_as_text(bool p_skip_cr) const {
542
uint64_t original_pos = get_position();
543
const_cast<FileAccess *>(this)->seek(0);
544
545
String text = get_as_utf8_string(p_skip_cr);
546
547
const_cast<FileAccess *>(this)->seek(original_pos);
548
549
return text;
550
}
551
552
Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
553
Vector<uint8_t> data;
554
555
ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0.");
556
if (p_length == 0) {
557
return data;
558
}
559
560
Error err = data.resize(p_length);
561
ERR_FAIL_COND_V_MSG(err != OK, data, vformat("Can't resize data to %d elements.", p_length));
562
563
uint8_t *w = data.ptrw();
564
int64_t len = get_buffer(w, p_length);
565
566
if (len < p_length) {
567
data.resize(len);
568
}
569
570
return data;
571
}
572
573
String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
574
Vector<uint8_t> sourcef;
575
uint64_t len = get_length();
576
sourcef.resize(len + 1);
577
578
uint8_t *w = sourcef.ptrw();
579
uint64_t r = get_buffer(w, len);
580
ERR_FAIL_COND_V(r != len, String());
581
w[len] = 0;
582
583
String s;
584
s.append_utf8((const char *)w, len, p_skip_cr);
585
return s;
586
}
587
588
bool FileAccess::store_8(uint8_t p_dest) {
589
return store_buffer(&p_dest, sizeof(uint8_t));
590
}
591
592
bool FileAccess::store_16(uint16_t p_dest) {
593
#ifdef BIG_ENDIAN_ENABLED
594
if (!big_endian) {
595
p_dest = BSWAP16(p_dest);
596
}
597
#else
598
if (big_endian) {
599
p_dest = BSWAP16(p_dest);
600
}
601
#endif
602
603
return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint16_t));
604
}
605
606
bool FileAccess::store_32(uint32_t p_dest) {
607
#ifdef BIG_ENDIAN_ENABLED
608
if (!big_endian) {
609
p_dest = BSWAP32(p_dest);
610
}
611
#else
612
if (big_endian) {
613
p_dest = BSWAP32(p_dest);
614
}
615
#endif
616
617
return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint32_t));
618
}
619
620
bool FileAccess::store_64(uint64_t p_dest) {
621
#ifdef BIG_ENDIAN_ENABLED
622
if (!big_endian) {
623
p_dest = BSWAP64(p_dest);
624
}
625
#else
626
if (big_endian) {
627
p_dest = BSWAP64(p_dest);
628
}
629
#endif
630
631
return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint64_t));
632
}
633
634
bool FileAccess::store_real(real_t p_real) {
635
if constexpr (sizeof(real_t) == 4) {
636
return store_float(p_real);
637
} else {
638
return store_double(p_real);
639
}
640
}
641
642
bool FileAccess::store_half(float p_dest) {
643
return store_16(Math::make_half_float(p_dest));
644
}
645
646
bool FileAccess::store_float(float p_dest) {
647
MarshallFloat m;
648
m.f = p_dest;
649
return store_32(m.i);
650
}
651
652
bool FileAccess::store_double(double p_dest) {
653
MarshallDouble m;
654
m.d = p_dest;
655
return store_64(m.l);
656
}
657
658
uint64_t FileAccess::get_modified_time(const String &p_file) {
659
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
660
return 0;
661
}
662
663
Ref<FileAccess> fa = create_for_path(p_file);
664
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));
665
666
return fa->_get_modified_time(p_file);
667
}
668
669
uint64_t FileAccess::get_access_time(const String &p_file) {
670
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
671
return 0;
672
}
673
674
Ref<FileAccess> fa = create_for_path(p_file);
675
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'.");
676
677
return fa->_get_access_time(p_file);
678
}
679
680
int64_t FileAccess::get_size(const String &p_file) {
681
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
682
return PackedData::get_singleton()->get_size(p_file);
683
}
684
685
Ref<FileAccess> fa = create_for_path(p_file);
686
ERR_FAIL_COND_V_MSG(fa.is_null(), -1, "Cannot create FileAccess for path '" + p_file + "'.");
687
688
return fa->_get_size(p_file);
689
}
690
691
BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const String &p_file) {
692
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
693
return 0;
694
}
695
696
Ref<FileAccess> fa = create_for_path(p_file);
697
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));
698
699
return fa->_get_unix_permissions(p_file);
700
}
701
702
Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
703
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
704
return ERR_UNAVAILABLE;
705
}
706
707
Ref<FileAccess> fa = create_for_path(p_file);
708
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
709
710
Error err = fa->_set_unix_permissions(p_file, p_permissions);
711
return err;
712
}
713
714
bool FileAccess::get_hidden_attribute(const String &p_file) {
715
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
716
return false;
717
}
718
719
Ref<FileAccess> fa = create_for_path(p_file);
720
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));
721
722
return fa->_get_hidden_attribute(p_file);
723
}
724
725
Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) {
726
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
727
return ERR_UNAVAILABLE;
728
}
729
730
Ref<FileAccess> fa = create_for_path(p_file);
731
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
732
733
Error err = fa->_set_hidden_attribute(p_file, p_hidden);
734
return err;
735
}
736
737
bool FileAccess::get_read_only_attribute(const String &p_file) {
738
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
739
return false;
740
}
741
742
Ref<FileAccess> fa = create_for_path(p_file);
743
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));
744
745
return fa->_get_read_only_attribute(p_file);
746
}
747
748
Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) {
749
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
750
return ERR_UNAVAILABLE;
751
}
752
753
Ref<FileAccess> fa = create_for_path(p_file);
754
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
755
756
Error err = fa->_set_read_only_attribute(p_file, p_ro);
757
return err;
758
}
759
760
bool FileAccess::store_string(const String &p_string) {
761
if (p_string.length() == 0) {
762
return true;
763
}
764
765
CharString cs = p_string.utf8();
766
return store_buffer((uint8_t *)&cs[0], cs.length());
767
}
768
769
bool FileAccess::store_pascal_string(const String &p_string) {
770
CharString cs = p_string.utf8();
771
return store_32(cs.length()) && store_buffer((uint8_t *)&cs[0], cs.length());
772
}
773
774
String FileAccess::get_pascal_string() {
775
uint32_t sl = get_32();
776
CharString cs;
777
cs.resize_uninitialized(sl + 1);
778
get_buffer((uint8_t *)cs.ptr(), sl);
779
cs[sl] = 0;
780
781
return String::utf8(cs.ptr(), sl);
782
}
783
784
bool FileAccess::store_line(const String &p_line) {
785
return store_string(p_line) && store_8('\n');
786
}
787
788
bool FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
789
ERR_FAIL_COND_V(p_delim.length() != 1, false);
790
791
String line = "";
792
int size = p_values.size();
793
for (int i = 0; i < size; ++i) {
794
String value = p_values[i];
795
796
if (value.contains_char('"') || value.contains(p_delim) || value.contains_char('\n')) {
797
value = "\"" + value.replace("\"", "\"\"") + "\"";
798
}
799
if (i < size - 1) {
800
value += p_delim;
801
}
802
803
line += value;
804
}
805
806
return store_line(line);
807
}
808
809
bool FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) {
810
uint64_t len = p_buffer.size();
811
if (len == 0) {
812
return true;
813
}
814
815
const uint8_t *r = p_buffer.ptr();
816
817
return store_buffer(r, len);
818
}
819
820
bool FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
821
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
822
for (uint64_t i = 0; i < p_length; i++) {
823
if (unlikely(!store_8(p_src[i]))) {
824
return false;
825
}
826
}
827
return true;
828
}
829
830
bool FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
831
int len;
832
Error err = encode_variant(p_var, nullptr, len, p_full_objects);
833
ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
834
835
Vector<uint8_t> buff;
836
buff.resize(len);
837
838
uint8_t *w = buff.ptrw();
839
err = encode_variant(p_var, &w[0], len, p_full_objects);
840
ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
841
842
return store_32(uint32_t(len)) && store_buffer(buff);
843
}
844
845
Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_error) {
846
Ref<FileAccess> f = FileAccess::open(p_path, READ, r_error);
847
if (f.is_null()) {
848
if (r_error) { // if error requested, do not throw error
849
return Vector<uint8_t>();
850
}
851
ERR_FAIL_V_MSG(Vector<uint8_t>(), vformat("Can't open file from path '%s'.", String(p_path)));
852
}
853
Vector<uint8_t> data;
854
data.resize(f->get_length());
855
f->get_buffer(data.ptrw(), data.size());
856
return data;
857
}
858
859
String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
860
Error err;
861
Vector<uint8_t> array = get_file_as_bytes(p_path, &err);
862
if (r_error) {
863
*r_error = err;
864
}
865
if (err != OK) {
866
if (r_error) {
867
return String();
868
}
869
ERR_FAIL_V_MSG(String(), vformat("Can't get file as string from path '%s'.", String(p_path)));
870
}
871
872
String ret;
873
ret.append_utf8((const char *)array.ptr(), array.size());
874
return ret;
875
}
876
877
String FileAccess::get_md5(const String &p_file) {
878
Ref<FileAccess> f = FileAccess::open(p_file, READ);
879
if (f.is_null()) {
880
return String();
881
}
882
883
CryptoCore::MD5Context ctx;
884
ctx.start();
885
886
unsigned char step[32768];
887
888
while (true) {
889
uint64_t br = f->get_buffer(step, 32768);
890
if (br > 0) {
891
ctx.update(step, br);
892
}
893
if (br < 4096) {
894
break;
895
}
896
}
897
898
unsigned char hash[16];
899
ctx.finish(hash);
900
901
return String::md5(hash);
902
}
903
904
String FileAccess::get_multiple_md5(const Vector<String> &p_file) {
905
CryptoCore::MD5Context ctx;
906
ctx.start();
907
908
for (int i = 0; i < p_file.size(); i++) {
909
Ref<FileAccess> f = FileAccess::open(p_file[i], READ);
910
ERR_CONTINUE(f.is_null());
911
912
unsigned char step[32768];
913
914
while (true) {
915
uint64_t br = f->get_buffer(step, 32768);
916
if (br > 0) {
917
ctx.update(step, br);
918
}
919
if (br < 4096) {
920
break;
921
}
922
}
923
}
924
925
unsigned char hash[16];
926
ctx.finish(hash);
927
928
return String::md5(hash);
929
}
930
931
String FileAccess::get_sha256(const String &p_file) {
932
Ref<FileAccess> f = FileAccess::open(p_file, READ);
933
if (f.is_null()) {
934
return String();
935
}
936
937
CryptoCore::SHA256Context ctx;
938
ctx.start();
939
940
unsigned char step[32768];
941
942
while (true) {
943
uint64_t br = f->get_buffer(step, 32768);
944
if (br > 0) {
945
ctx.update(step, br);
946
}
947
if (br < 4096) {
948
break;
949
}
950
}
951
952
unsigned char hash[32];
953
ctx.finish(hash);
954
955
return String::hex_encode_buffer(hash, 32);
956
}
957
958
void FileAccess::_bind_methods() {
959
ClassDB::bind_static_method("FileAccess", D_METHOD("open", "path", "flags"), &FileAccess::_open);
960
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key", "iv"), &FileAccess::open_encrypted, DEFVAL(Vector<uint8_t>()));
961
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass);
962
ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0));
963
ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error);
964
ClassDB::bind_static_method("FileAccess", D_METHOD("create_temp", "mode_flags", "prefix", "extension", "keep"), &FileAccess::_create_temp, DEFVAL(""), DEFVAL(""), DEFVAL(false));
965
966
ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_bytes", "path"), &FileAccess::_get_file_as_bytes);
967
ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_string", "path"), &FileAccess::_get_file_as_string);
968
969
ClassDB::bind_method(D_METHOD("resize", "length"), &FileAccess::resize);
970
ClassDB::bind_method(D_METHOD("flush"), &FileAccess::flush);
971
ClassDB::bind_method(D_METHOD("get_path"), &FileAccess::get_path);
972
ClassDB::bind_method(D_METHOD("get_path_absolute"), &FileAccess::get_path_absolute);
973
ClassDB::bind_method(D_METHOD("is_open"), &FileAccess::is_open);
974
ClassDB::bind_method(D_METHOD("seek", "position"), &FileAccess::seek);
975
ClassDB::bind_method(D_METHOD("seek_end", "position"), &FileAccess::seek_end, DEFVAL(0));
976
ClassDB::bind_method(D_METHOD("get_position"), &FileAccess::get_position);
977
ClassDB::bind_method(D_METHOD("get_length"), &FileAccess::get_length);
978
ClassDB::bind_method(D_METHOD("eof_reached"), &FileAccess::eof_reached);
979
ClassDB::bind_method(D_METHOD("get_8"), &FileAccess::get_8);
980
ClassDB::bind_method(D_METHOD("get_16"), &FileAccess::get_16);
981
ClassDB::bind_method(D_METHOD("get_32"), &FileAccess::get_32);
982
ClassDB::bind_method(D_METHOD("get_64"), &FileAccess::get_64);
983
ClassDB::bind_method(D_METHOD("get_half"), &FileAccess::get_half);
984
ClassDB::bind_method(D_METHOD("get_float"), &FileAccess::get_float);
985
ClassDB::bind_method(D_METHOD("get_double"), &FileAccess::get_double);
986
ClassDB::bind_method(D_METHOD("get_real"), &FileAccess::get_real);
987
ClassDB::bind_method(D_METHOD("get_buffer", "length"), (Vector<uint8_t> (FileAccess::*)(int64_t) const) & FileAccess::get_buffer);
988
ClassDB::bind_method(D_METHOD("get_line"), &FileAccess::get_line);
989
ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &FileAccess::get_csv_line, DEFVAL(","));
990
ClassDB::bind_method(D_METHOD("get_as_text", "skip_cr"), &FileAccess::get_as_text, DEFVAL(false));
991
ClassDB::bind_static_method("FileAccess", D_METHOD("get_md5", "path"), &FileAccess::get_md5);
992
ClassDB::bind_static_method("FileAccess", D_METHOD("get_sha256", "path"), &FileAccess::get_sha256);
993
ClassDB::bind_method(D_METHOD("is_big_endian"), &FileAccess::is_big_endian);
994
ClassDB::bind_method(D_METHOD("set_big_endian", "big_endian"), &FileAccess::set_big_endian);
995
ClassDB::bind_method(D_METHOD("get_error"), &FileAccess::get_error);
996
ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &FileAccess::get_var, DEFVAL(false));
997
998
ClassDB::bind_method(D_METHOD("store_8", "value"), &FileAccess::store_8);
999
ClassDB::bind_method(D_METHOD("store_16", "value"), &FileAccess::store_16);
1000
ClassDB::bind_method(D_METHOD("store_32", "value"), &FileAccess::store_32);
1001
ClassDB::bind_method(D_METHOD("store_64", "value"), &FileAccess::store_64);
1002
ClassDB::bind_method(D_METHOD("store_half", "value"), &FileAccess::store_half);
1003
ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float);
1004
ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double);
1005
ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real);
1006
ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (bool (FileAccess::*)(const Vector<uint8_t> &))&FileAccess::store_buffer);
1007
ClassDB::bind_method(D_METHOD("store_line", "line"), &FileAccess::store_line);
1008
ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line, DEFVAL(","));
1009
ClassDB::bind_method(D_METHOD("store_string", "string"), &FileAccess::store_string);
1010
ClassDB::bind_method(D_METHOD("store_var", "value", "full_objects"), &FileAccess::store_var, DEFVAL(false));
1011
1012
ClassDB::bind_method(D_METHOD("store_pascal_string", "string"), &FileAccess::store_pascal_string);
1013
ClassDB::bind_method(D_METHOD("get_pascal_string"), &FileAccess::get_pascal_string);
1014
1015
ClassDB::bind_method(D_METHOD("close"), &FileAccess::close);
1016
1017
ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists);
1018
ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time);
1019
ClassDB::bind_static_method("FileAccess", D_METHOD("get_access_time", "file"), &FileAccess::get_access_time);
1020
ClassDB::bind_static_method("FileAccess", D_METHOD("get_size", "file"), &FileAccess::get_size);
1021
1022
ClassDB::bind_static_method("FileAccess", D_METHOD("get_unix_permissions", "file"), &FileAccess::get_unix_permissions);
1023
ClassDB::bind_static_method("FileAccess", D_METHOD("set_unix_permissions", "file", "permissions"), &FileAccess::set_unix_permissions);
1024
1025
ClassDB::bind_static_method("FileAccess", D_METHOD("get_hidden_attribute", "file"), &FileAccess::get_hidden_attribute);
1026
ClassDB::bind_static_method("FileAccess", D_METHOD("set_hidden_attribute", "file", "hidden"), &FileAccess::set_hidden_attribute);
1027
ClassDB::bind_static_method("FileAccess", D_METHOD("set_read_only_attribute", "file", "ro"), &FileAccess::set_read_only_attribute);
1028
ClassDB::bind_static_method("FileAccess", D_METHOD("get_read_only_attribute", "file"), &FileAccess::get_read_only_attribute);
1029
1030
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian");
1031
1032
BIND_ENUM_CONSTANT(READ);
1033
BIND_ENUM_CONSTANT(WRITE);
1034
BIND_ENUM_CONSTANT(READ_WRITE);
1035
BIND_ENUM_CONSTANT(WRITE_READ);
1036
1037
BIND_ENUM_CONSTANT(COMPRESSION_FASTLZ);
1038
BIND_ENUM_CONSTANT(COMPRESSION_DEFLATE);
1039
BIND_ENUM_CONSTANT(COMPRESSION_ZSTD);
1040
BIND_ENUM_CONSTANT(COMPRESSION_GZIP);
1041
BIND_ENUM_CONSTANT(COMPRESSION_BROTLI);
1042
1043
BIND_BITFIELD_FLAG(UNIX_READ_OWNER);
1044
BIND_BITFIELD_FLAG(UNIX_WRITE_OWNER);
1045
BIND_BITFIELD_FLAG(UNIX_EXECUTE_OWNER);
1046
BIND_BITFIELD_FLAG(UNIX_READ_GROUP);
1047
BIND_BITFIELD_FLAG(UNIX_WRITE_GROUP);
1048
BIND_BITFIELD_FLAG(UNIX_EXECUTE_GROUP);
1049
BIND_BITFIELD_FLAG(UNIX_READ_OTHER);
1050
BIND_BITFIELD_FLAG(UNIX_WRITE_OTHER);
1051
BIND_BITFIELD_FLAG(UNIX_EXECUTE_OTHER);
1052
BIND_BITFIELD_FLAG(UNIX_SET_USER_ID);
1053
BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID);
1054
BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE);
1055
}
1056
1057
FileAccess::~FileAccess() {
1058
_delete_temp();
1059
}
1060
1061