forked from realXtend/tundra
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLocalAssetProvider.cpp
276 lines (230 loc) · 9.68 KB
/
LocalAssetProvider.cpp
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// For conditions of distribution and use, see copyright notice in license.txt
#include "StableHeaders.h"
#include "DebugOperatorNew.h"
#include "MemoryLeakCheck.h"
#include "AssetModule.h"
#include "CoreTypes.h"
#include "LocalAssetProvider.h"
#include "Framework.h"
#include "EventManager.h"
#include "ServiceManager.h"
#include "ConfigurationManager.h"
#include "LocalAssetStorage.h"
#include "IAssetUploadTransfer.h"
#include "IAssetTransfer.h"
#include "AssetAPI.h"
#include <QByteArray>
#include <QFile>
#include <QFileInfo>
#include <QFileSystemWatcher>
namespace Asset
{
LocalAssetProvider::LocalAssetProvider(Foundation::Framework* framework_)
:framework(framework_)
{
}
LocalAssetProvider::~LocalAssetProvider()
{
}
QString LocalAssetProvider::Name()
{
static const QString name("Local");
return name;
}
bool LocalAssetProvider::IsValidRef(QString asset_id, QString asset_type)
{
QString ref = asset_id.toStdString().c_str();
ref = ref.trimmed();
if (ref.length() == 0)
return false;
if (ref.startsWith("file://") || ref.startsWith("local://"))
return true;
if (!ref.contains("://")) // If the ref doesn't contain a protocol specifier (we do this simple check for it), try to directly find the given local file.
{
QString path = GetPathForAsset(ref, 0);
return path.length() > 0;
}
else
return false;
}
AssetTransferPtr LocalAssetProvider::RequestAsset(QString assetRef, QString assetType)
{
if (assetRef.isEmpty())
return AssetTransferPtr();
AssetTransferPtr transfer = AssetTransferPtr(new IAssetTransfer);
transfer->source.ref = assetRef.trimmed();
transfer->assetType = assetType;
pendingDownloads.push_back(transfer);
return transfer;
}
QString LocalAssetProvider::GetPathForAsset(const QString &assetname, LocalAssetStoragePtr *storage)
{
// Check first all subdirs without recursion, because recursion is potentially slow
for(size_t i = 0; i < storages.size(); ++i)
{
QString path = storages[i]->GetFullPathForAsset(assetname.toStdString().c_str(), false);
if (path != "")
{
if (storage)
*storage = storages[i];
return path;
}
}
for(size_t i = 0; i < storages.size(); ++i)
{
QString path = storages[i]->GetFullPathForAsset(assetname.toStdString().c_str(), true);
if (path != "")
{
if (storage)
*storage = storages[i];
return path;
}
}
if (storage)
*storage = LocalAssetStoragePtr();
return "";
}
void LocalAssetProvider::Update(f64 frametime)
{
///\note It is *very* important that below we first complete all uploads, and then the downloads.
/// This is because it is a rather common code flow to upload an asset for an entity, and immediately after that
/// generate a entity in the scene that refers to that asset, which means we do both an upload and a download of the
/// asset into the same asset storage. If the download request was processed before the upload request, the download
/// request would fail on missing file, and the entity would erroneously get an "asset not found" result.
CompletePendingFileUploads();
CompletePendingFileDownloads();
}
void LocalAssetProvider::DeleteAssetFromStorage(QString assetRef)
{
if (!assetRef.isEmpty())
QFile::remove(assetRef); ///\todo Check here that the assetRef points to one of the accepted storage directories, and don't allow deleting anything else.
AssetModule::LogInfo("LocalAssetProvider::DeleteAssetFromStorage: Deleted asset file \"" + assetRef.toStdString() + "\" from disk.");
}
void LocalAssetProvider::AddStorageDirectory(const std::string &directory, const std::string &storageName, bool recursive)
{
///\todo Check first if the given directory exists as a storage, and don't add it as a duplicate if so.
LocalAssetStoragePtr storage = LocalAssetStoragePtr(new LocalAssetStorage());
storage->directory = directory.c_str();
storage->name = storageName.c_str();
storage->recursive = recursive;
storage->provider = shared_from_this();
storage->SetupWatcher(); // Start listening on file change notifications.
// connect(storage->changeWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(FileChanged(QString)));
// connect(storage->changeWatcher, SIGNAL(fileChanged(QString)), this, SLOT(FileChanged(QString)));
storages.push_back(storage);
}
std::vector<AssetStoragePtr> LocalAssetProvider::GetStorages() const
{
std::vector<AssetStoragePtr> stores;
for(size_t i = 0; i < storages.size(); ++i)
stores.push_back(storages[i]);
return stores;
}
AssetUploadTransferPtr LocalAssetProvider::UploadAssetFromFileInMemory(const u8 *data, size_t numBytes, AssetStoragePtr destination, const char *assetName)
{
assert(data);
if (!data)
{
AssetModule::LogError("LocalAssetProvider::UploadAssetFromFileInMemory: Null source data pointer passed to function!");
return AssetUploadTransferPtr();
}
LocalAssetStorage *storage = dynamic_cast<LocalAssetStorage*>(destination.get());
if (!storage)
{
AssetModule::LogError("LocalAssetProvider::UploadAssetFromFileInMemory: Invalid destination asset storage type! Was not of type LocalAssetStorage!");
return AssetUploadTransferPtr();
}
AssetUploadTransferPtr transfer = AssetUploadTransferPtr(new IAssetUploadTransfer());
transfer->sourceFilename = "";
transfer->destinationName = assetName;
transfer->destinationStorage = destination;
transfer->assetData.insert(transfer->assetData.end(), data, data + numBytes);
pendingUploads.push_back(transfer);
return transfer;
}
void LocalAssetProvider::CompletePendingFileDownloads()
{
while(pendingDownloads.size() > 0)
{
AssetTransferPtr transfer = pendingDownloads.back();
pendingDownloads.pop_back();
QString ref = transfer->source.ref;
// AssetModule::LogDebug("New local asset request: " + ref.toStdString());
// Strip file: trims asset provider id (f.ex. 'file://') and potential mesh name inside the file (everything after last slash)
if (ref.startsWith("file://"))
ref = ref.mid(7);
if (ref.startsWith("local://"))
ref = ref.mid(8);
int lastSlash = ref.lastIndexOf('/');
if (lastSlash != -1)
ref = ref.left(lastSlash);
LocalAssetStoragePtr storage;
QString path = GetPathForAsset(ref, &storage);
if (path.isEmpty())
{
QString reason = "Failed to find local asset with filename \"" + ref + "\"!";
// AssetModule::LogWarning(reason.toStdString());
framework->Asset()->AssetTransferFailed(transfer.get(), reason);
continue;
}
QFileInfo file(GuaranteeTrailingSlash(path) + ref);
QString absoluteFilename = file.absoluteFilePath();
bool success = LoadFileToVector(absoluteFilename.toStdString().c_str(), transfer->rawAssetData);
if (!success)
{
QString reason = "Failed to read asset data for asset \"" + ref + "\" from file \"" + absoluteFilename + "\"";
// AssetModule::LogError(reason.toStdString());
framework->Asset()->AssetTransferFailed(transfer.get(), reason);
continue;
}
// Tell the Asset API that this asset should not be cached into the asset cache, and instead the original filename should be used
// as a disk source, rather than generating a cache file for it.
transfer->SetCachingBehavior(false, absoluteFilename.toStdString().c_str());
transfer->storage = storage;
// AssetModule::LogDebug("Downloaded asset \"" + ref.toStdString() + "\" from file " + absoluteFilename.toStdString());
// Signal the Asset API that this asset is now successfully downloaded.
framework->Asset()->AssetTransferCompleted(transfer.get());
}
}
void LocalAssetProvider::CompletePendingFileUploads()
{
while(pendingUploads.size() > 0)
{
AssetUploadTransferPtr transfer = pendingUploads.back();
pendingUploads.pop_back();
LocalAssetStoragePtr storage = boost::dynamic_pointer_cast<LocalAssetStorage>(transfer->destinationStorage.lock());
if (!storage)
{
AssetModule::LogError("Invalid IAssetStorage specified for file upload in LocalAssetProvider!");
transfer->EmitTransferFailed();
continue;
}
if (transfer->sourceFilename.length() == 0 && transfer->assetData.size() == 0)
{
AssetModule::LogError("No source data present when trying to upload asset to LocalAssetProvider!");
continue;
}
QString fromFile = transfer->sourceFilename;
QString toFile = GuaranteeTrailingSlash(storage->directory) + transfer->destinationName;
bool success;
if (fromFile.length() == 0)
success = SaveAssetFromMemoryToFile(&transfer->assetData[0], transfer->assetData.size(), toFile.toStdString().c_str());
else
success = CopyAssetFile(fromFile.toStdString().c_str(), toFile.toStdString().c_str());
if (!success)
{
AssetModule::LogError(("Asset upload failed in LocalAssetProvider: CopyAsset from \"" + fromFile + "\" to \"" + toFile + "\" failed!").toStdString());
transfer->EmitTransferFailed();
/// \todo Jukka lisää failure-notifikaatio.
}
else
{
framework->Asset()->AssetUploadTransferCompleted(transfer.get());
}
}
}
void LocalAssetProvider::FileChanged(const QString &path)
{
AssetModule::LogInfo(("File " + path + " changed.").toStdString());
}
} // ~Asset