Skip to content

Commit 8ac6823

Browse files
committed
Implemented unload of PCK files
1 parent e65a237 commit 8ac6823

8 files changed

+225
-23
lines changed

core/config/project_settings.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,27 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f
493493
return true;
494494
}
495495

496+
bool ProjectSettings::_unload_resource_pack(const String &p_pack) {
497+
if (PackedData::get_singleton()->is_disabled()) {
498+
return false;
499+
}
500+
501+
bool ok = PackedData::get_singleton()->remove_pack(p_pack) == OK;
502+
if (!ok) {
503+
return false;
504+
}
505+
506+
return true;
507+
}
508+
509+
bool ProjectSettings::_is_pack_loaded(const String &p_pack) {
510+
if (PackedData::get_singleton()->is_disabled()) {
511+
return false;
512+
}
513+
514+
return PackedData::get_singleton()->is_pack_loaded(p_pack);
515+
}
516+
496517
void ProjectSettings::_convert_to_last_version(int p_from_version) {
497518
#ifndef DISABLE_DEPRECATED
498519
if (p_from_version <= 3) {
@@ -1389,6 +1410,8 @@ void ProjectSettings::_bind_methods() {
13891410
ClassDB::bind_method(D_METHOD("globalize_path", "path"), &ProjectSettings::globalize_path);
13901411
ClassDB::bind_method(D_METHOD("save"), &ProjectSettings::save);
13911412
ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0));
1413+
ClassDB::bind_method(D_METHOD("unload_resource_pack", "pack"), &ProjectSettings::_unload_resource_pack);
1414+
ClassDB::bind_method(D_METHOD("is_pack_loaded", "name"), &ProjectSettings::_is_pack_loaded);
13921415

13931416
ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd);
13941417

core/config/project_settings.h

+2
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ class ProjectSettings : public Object {
138138
void _convert_to_last_version(int p_from_version);
139139

140140
bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0);
141+
bool _unload_resource_pack(const String &p_pack);
142+
bool _is_pack_loaded(const String &p_pack);
141143

142144
void _add_property_info_bind(const Dictionary &p_info);
143145

core/io/file_access_pack.cpp

+134-6
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@
3131
#include "file_access_pack.h"
3232

3333
#include "core/io/file_access_encrypted.h"
34+
#include "core/io/resource.h"
3435
#include "core/object/script_language.h"
3536
#include "core/os/os.h"
3637
#include "core/version.h"
3738

39+
#include "modules/modules_enabled.gen.h" // For gdscript.
40+
#ifdef MODULE_GDSCRIPT_ENABLED
41+
#include "modules/gdscript/gdscript_cache.h"
42+
#endif
43+
3844
#include <stdio.h>
3945

4046
Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) {
@@ -47,29 +53,86 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t
4753
return ERR_FILE_UNRECOGNIZED;
4854
}
4955

56+
Error PackedData::remove_pack(const String &p_path) {
57+
if (!is_pack_loaded(p_path)) {
58+
return ERR_FILE_UNRECOGNIZED;
59+
}
60+
61+
Vector<PathMD5> reload_packs;
62+
Vector<HashMap<PathMD5, PackedFile, PathMD5>::Iterator> to_remove;
63+
for (HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.begin(); E; ++E) {
64+
PackedFile pf = E->value;
65+
if (pf.pack.name != p_path) {
66+
continue;
67+
}
68+
69+
PathMD5 pmd5 = pf.pack.replaced_pack;
70+
if (pmd5.set) {
71+
if (!reload_packs.has(pmd5)) {
72+
reload_packs.push_back(pmd5);
73+
}
74+
}
75+
76+
remove_path(pf.filepath);
77+
to_remove.push_back(E);
78+
}
79+
80+
for (const HashMap<PathMD5, PackedFile, PathMD5>::Iterator &E : to_remove) {
81+
files.remove(E);
82+
}
83+
remove_loaded_pack(p_path);
84+
85+
// For reloading files unloaded by more recent packs, we simply need to reload packs with replace_files disabled.
86+
for (PathMD5 pmd5 : reload_packs) {
87+
if (!loaded_packs.has(pmd5)) {
88+
continue;
89+
}
90+
LoadedPackInfo pack_info = loaded_packs[pmd5];
91+
Error err = add_pack(pack_info.name, false, pack_info.offset);
92+
if (err) {
93+
return err;
94+
}
95+
}
96+
97+
return OK;
98+
}
99+
50100
void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) {
51101
String simplified_path = p_path.simplify_path();
52102
PathMD5 pmd5(simplified_path.md5_buffer());
53103

104+
// When running on the editor, the base files are not loaded from a main pack file.
105+
// This extra check prevents packs from overriding those base files.
106+
#ifdef TOOLS_ENABLED
107+
bool exists = files.has(pmd5) || FileAccess::exists(simplified_path);
108+
#else
54109
bool exists = files.has(pmd5);
110+
#endif
111+
112+
PackInfo pi;
113+
pi.name = p_pkg_path;
114+
if (p_replace_files && exists) {
115+
pi.replaced_pack = PathMD5(files[pmd5].pack.name.md5_buffer());
116+
}
55117

56118
PackedFile pf;
57119
pf.encrypted = p_encrypted;
58-
pf.pack = p_pkg_path;
120+
pf.pack = pi;
59121
pf.offset = p_ofs;
60122
pf.size = p_size;
61123
for (int i = 0; i < 16; i++) {
62124
pf.md5[i] = p_md5[i];
63125
}
64126
pf.src = p_src;
127+
pf.filepath = simplified_path.replace_first("res://", "");
65128

66129
if (!exists || p_replace_files) {
67130
files[pmd5] = pf;
68131
}
69132

70133
if (!exists) {
71134
//search for dir
72-
String p = simplified_path.replace_first("res://", "");
135+
String p = pf.filepath;
73136
PackedDir *cd = root;
74137

75138
if (p.contains("/")) { //in a subdir
@@ -96,6 +159,69 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
96159
}
97160
}
98161

162+
void PackedData::remove_path(const String &p_path) {
163+
String p = p_path;
164+
PackedDir *cd = root;
165+
166+
if (p.contains("/")) {
167+
Vector<String> ds = p.get_base_dir().split("/");
168+
169+
for (int j = 0; j < ds.size(); j++) {
170+
if (!cd->subdirs.has(ds[j])) {
171+
return;
172+
} else {
173+
cd = cd->subdirs[ds[j]];
174+
}
175+
}
176+
}
177+
String filename = p_path.get_file();
178+
cd->files.erase(filename);
179+
180+
// Clear empty folders.
181+
while (cd && cd->files.is_empty() && cd->subdirs.is_empty()) {
182+
String name = cd->name;
183+
cd = cd->parent;
184+
if (cd) {
185+
cd->subdirs.erase(name);
186+
}
187+
}
188+
189+
String res_path = "res://" + p_path;
190+
191+
// Remove paths from cache.
192+
if (ResourceCache::has(res_path)) {
193+
ResourceCache::remove_cached_resource(res_path);
194+
}
195+
196+
// GDScript also caches scripts internally, so they too must be removed.
197+
#ifdef MODULE_GDSCRIPT_ENABLED
198+
if (GDScriptCache::get_cached_script(res_path).is_valid()) {
199+
GDScriptCache::remove_script(res_path);
200+
}
201+
#endif
202+
}
203+
204+
void PackedData::add_loaded_pack(const String &p_path, const uint64_t &p_offset) {
205+
if (!is_pack_loaded(p_path)) {
206+
LoadedPackInfo pack_info;
207+
pack_info.name = p_path;
208+
pack_info.offset = p_offset;
209+
210+
PathMD5 pmd5(p_path.md5_buffer());
211+
loaded_packs.insert(pmd5, pack_info);
212+
}
213+
}
214+
215+
void PackedData::remove_loaded_pack(const String &p_path) {
216+
PathMD5 pmd5(p_path.md5_buffer());
217+
loaded_packs.erase(pmd5);
218+
}
219+
220+
bool PackedData::is_pack_loaded(const String &p_pack_path) const {
221+
PathMD5 pmd5(p_pack_path.md5_buffer());
222+
return loaded_packs.has(pmd5);
223+
}
224+
99225
void PackedData::add_pack_source(PackSource *p_source) {
100226
if (p_source != nullptr) {
101227
sources.push_back(p_source);
@@ -259,6 +385,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
259385
f = fae;
260386
}
261387

388+
PackedData::get_singleton()->add_loaded_pack(p_path, p_offset);
389+
262390
for (int i = 0; i < file_count; i++) {
263391
uint32_t sl = f->get_32();
264392
CharString cs;
@@ -385,16 +513,16 @@ void FileAccessPack::close() {
385513

386514
FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) :
387515
pf(p_file),
388-
f(FileAccess::open(pf.pack, FileAccess::READ)) {
389-
ERR_FAIL_COND_MSG(f.is_null(), vformat("Can't open pack-referenced file '%s'.", String(pf.pack)));
516+
f(FileAccess::open(pf.pack.name, FileAccess::READ)) {
517+
ERR_FAIL_COND_MSG(f.is_null(), vformat("Can't open pack-referenced file '%s'.", String(pf.pack.name)));
390518

391519
f->seek(pf.offset);
392520
off = pf.offset;
393521

394522
if (pf.encrypted) {
395523
Ref<FileAccessEncrypted> fae;
396524
fae.instantiate();
397-
ERR_FAIL_COND_MSG(fae.is_null(), vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack)));
525+
ERR_FAIL_COND_MSG(fae.is_null(), vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack.name)));
398526

399527
Vector<uint8_t> key;
400528
key.resize(32);
@@ -403,7 +531,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil
403531
}
404532

405533
Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false);
406-
ERR_FAIL_COND_MSG(err, vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack)));
534+
ERR_FAIL_COND_MSG(err, vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack.name)));
407535
f = fae;
408536
off = 0;
409537
}

core/io/file_access_pack.h

+40-17
Original file line numberDiff line numberDiff line change
@@ -60,30 +60,17 @@ class PackedData {
6060
friend class PackSource;
6161

6262
public:
63-
struct PackedFile {
64-
String pack;
65-
uint64_t offset; //if offset is ZERO, the file was ERASED
66-
uint64_t size;
67-
uint8_t md5[16];
68-
PackSource *src = nullptr;
69-
bool encrypted;
70-
};
71-
72-
private:
73-
struct PackedDir {
74-
PackedDir *parent = nullptr;
75-
String name;
76-
HashMap<String, PackedDir *> subdirs;
77-
HashSet<String> files;
78-
};
79-
8063
struct PathMD5 {
8164
uint64_t a = 0;
8265
uint64_t b = 0;
66+
bool set = false;
8367

8468
bool operator==(const PathMD5 &p_val) const {
8569
return (a == p_val.a) && (b == p_val.b);
8670
}
71+
bool operator!=(const PathMD5 &p_val) const {
72+
return (a != p_val.a) || (b != p_val.b);
73+
}
8774
static uint32_t hash(const PathMD5 &p_val) {
8875
uint32_t h = hash_murmur3_one_32(p_val.a);
8976
return hash_fmix32(hash_murmur3_one_32(p_val.b, h));
@@ -94,10 +81,40 @@ class PackedData {
9481
explicit PathMD5(const Vector<uint8_t> &p_buf) {
9582
a = *((uint64_t *)&p_buf[0]);
9683
b = *((uint64_t *)&p_buf[8]);
84+
set = true;
9785
}
9886
};
9987

88+
struct PackInfo {
89+
String name;
90+
PathMD5 replaced_pack;
91+
};
92+
93+
struct PackedFile {
94+
PackInfo pack;
95+
String filepath;
96+
uint64_t offset; //if offset is ZERO, the file was ERASED
97+
uint64_t size;
98+
uint8_t md5[16];
99+
PackSource *src = nullptr;
100+
bool encrypted;
101+
};
102+
103+
private:
104+
struct PackedDir {
105+
PackedDir *parent = nullptr;
106+
String name;
107+
HashMap<String, PackedDir *> subdirs;
108+
HashSet<String> files;
109+
};
110+
111+
struct LoadedPackInfo {
112+
String name;
113+
uint64_t offset;
114+
};
115+
100116
HashMap<PathMD5, PackedFile, PathMD5> files;
117+
HashMap<PathMD5, LoadedPackInfo, PathMD5> loaded_packs;
101118

102119
Vector<PackSource *> sources;
103120

@@ -111,13 +128,19 @@ class PackedData {
111128
public:
112129
void add_pack_source(PackSource *p_source);
113130
void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
131+
void remove_path(const String &p_path);
114132
uint8_t *get_file_hash(const String &p_path);
115133

134+
void add_loaded_pack(const String &p_path, const uint64_t &p_offset);
135+
void remove_loaded_pack(const String &p_path);
136+
bool is_pack_loaded(const String &p_pack_path) const;
137+
116138
void set_disabled(bool p_disabled) { disabled = p_disabled; }
117139
_FORCE_INLINE_ bool is_disabled() const { return disabled; }
118140

119141
static PackedData *get_singleton() { return singleton; }
120142
Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset);
143+
Error remove_pack(const String &p_path);
121144

122145
void clear();
123146

core/io/file_access_zip.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint6
180180
packages.push_back(pkg);
181181
int pkg_num = packages.size() - 1;
182182

183+
PackedData::get_singleton()->add_loaded_pack(p_path, 0);
184+
183185
for (uint64_t i = 0; i < gi.number_entry; i++) {
184186
char filename_inzip[256];
185187

core/io/resource.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,12 @@ void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) {
689689
}
690690
}
691691

692+
void ResourceCache::remove_cached_resource(const String &p_path) {
693+
lock.lock();
694+
resources.erase(p_path);
695+
lock.unlock();
696+
}
697+
692698
int ResourceCache::get_cached_resource_count() {
693699
MutexLock mutex_lock(lock);
694700
return resources.size();

core/io/resource.h

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class ResourceCache {
173173
static bool has(const String &p_path);
174174
static Ref<Resource> get_ref(const String &p_path);
175175
static void get_cached_resources(List<Ref<Resource>> *p_resources);
176+
static void remove_cached_resource(const String &p_path);
176177
static int get_cached_resource_count();
177178
};
178179

0 commit comments

Comments
 (0)