Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for adding natively embedded resources at runtime #8159

Merged
merged 1 commit into from Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 29 additions & 5 deletions engine/resource/src/dmsdk/resource/resource.h
Expand Up @@ -3,10 +3,10 @@
// Copyright 2009-2014 Ragnar Svensson, Christian Murray
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
Expand Down Expand Up @@ -258,7 +258,7 @@ namespace dmResource

/**
* Register a resource type
* @param factory Factory handle
* @param factory [type: dmResource::HFactory] Factory handle
* @param extension File extension for resource
* @param context User context
* @param preload_function Preload function. Optional, 0 if no preloading is used
Expand Down Expand Up @@ -497,7 +497,7 @@ namespace dmResource

/*#
* Returns the canonical path hash of a resource
* @param factory Factory handle
* @param factory [type: dmResource::HFactory] Factory handle
* @param resource Resource
* @param hash Returned hash
* @return RESULT_OK on success
Expand All @@ -515,11 +515,35 @@ namespace dmResource
typedef Result (*FDecryptResource)(void* buffer, uint32_t buffer_len);

/*#
* Returns the canonical path hash of a resource
* Registers a custom resource decryption function
* @name RegisterResourceDecryptionFunction
* @param decrypt_resource [type: dmResource::FDecryptResource] The decryption function
*/
void RegisterResourceDecryptionFunction(FDecryptResource decrypt_resource);

/*#
* Adds a file to the resource system
* Any request for this path will go through any existing mounts first.
* If you wish to provide file overrides, please use the LiveUpdate feature for that.
* The file isn't persisted between sessions.
*
* @name AddFile
* @param factory [type: dmResource::HFactory] Factory handle
* @param path [type: const char*] The path of the resource
* @param size [type: uint32_t] The size of the resource (in bytes)
* @param resource [type: const void*] The resource payload
* @return RESULT_OK on success.
*/
Result AddFile(HFactory factory, const char* path, uint32_t size, const void* resource);

/*#
* Removes a previously registered file from the resource system
* @name RemoveFile
* @param factory [type: dmResource::HFactory] Factory handle
* @param path [type: const char*] The path of the resource
* @return RESULT_OK on success.
*/
Result RemoveFile(HFactory factory, const char* path);
}

#endif // DMSDK_RESOURCE_H
12 changes: 12 additions & 0 deletions engine/resource/src/resource.cpp
Expand Up @@ -1312,6 +1312,18 @@ Result GetPath(HFactory factory, const void* resource, uint64_t* hash)
return RESULT_RESOURCE_NOT_FOUND;
}

Result AddFile(HFactory factory, const char* path, uint32_t size, const void* resource)
{
dmResourceMounts::HContext mounts = GetMountsContext(factory);
return dmResourceMounts::AddFile(mounts, dmHashString64(path), size, resource);
}

Result RemoveFile(HFactory factory, const char* path)
{
dmResourceMounts::HContext mounts = GetMountsContext(factory);
return dmResourceMounts::RemoveFile(mounts, dmHashString64(path));
}

dmMutex::HMutex GetLoadMutex(const dmResource::HFactory factory)
{
return factory->m_LoadMutex;
Expand Down
101 changes: 100 additions & 1 deletion engine/resource/src/resource_mounts.cpp
Expand Up @@ -33,12 +33,20 @@ struct ArchiveMount
bool m_Persist;
};

struct CustomFile
{
const void* m_Resource;
uint32_t m_Size;
};

struct ResourceMountsContext
{
// The currently mounted archives, in sorted order
dmArray<ArchiveMount> m_Mounts;
dmHashTable64<CustomFile> m_CustomFiles;
dmResourceProvider::HArchive m_ResourceBaseArchive;
dmMutex::HMutex m_Mutex;

};


Expand Down Expand Up @@ -69,6 +77,8 @@ void Destroy(HContext ctx)
{
DM_MUTEX_SCOPED_LOCK(ctx->m_Mutex);
DestroyMounts(ctx);

ctx->m_CustomFiles.Clear();
}
dmMutex::Delete(ctx->m_Mutex);
delete ctx;
Expand Down Expand Up @@ -348,6 +358,37 @@ static dmResource::Result DestroyMounts(HContext ctx)
return dmResource::RESULT_OK;
}

static dmResource::Result GetCustomResourceSize(HContext ctx, dmhash_t path_hash, const char* path, uint32_t* resource_size)
{
// Lock should already be held
CustomFile* file = ctx->m_CustomFiles.Get(path_hash);
if (file)
{
*resource_size = file->m_Size;
DM_RESOURCE_DBG_LOG(3, "GetResourceSize OK: %s " DM_HASH_FMT " (%u bytes) (custom file)\n", path, path_hash, *resource_size);
return dmResource::RESULT_OK;
}

return dmResource::RESULT_RESOURCE_NOT_FOUND;
}

static dmResource::Result ReadCustomResource(HContext ctx, dmhash_t path_hash, uint8_t* buffer, uint32_t buffer_size)
{
// Lock should already be held
CustomFile* file = ctx->m_CustomFiles.Get(path_hash);
if (file)
{
if (file->m_Size > buffer_size)
return dmResource::RESULT_INVAL; // Shouldn't really happen as we just queried the size

memcpy(buffer, file->m_Resource, buffer_size);

DM_RESOURCE_DBG_LOG(3, "ReadResource OK: %s " DM_HASH_FMT " (%u bytes) (custom file)\n", path_hash, buffer_size);
return dmResource::RESULT_OK;
}
return dmResource::RESULT_RESOURCE_NOT_FOUND;
}

dmResource::Result GetResourceSize(HContext ctx, dmhash_t path_hash, const char* path, uint32_t* resource_size)
{
DM_MUTEX_SCOPED_LOCK(ctx->m_Mutex);
Expand All @@ -367,6 +408,10 @@ dmResource::Result GetResourceSize(HContext ctx, dmhash_t path_hash, const char*
}
return ProviderResultToResult(result);
}

if (!ctx->m_CustomFiles.Empty())
return GetCustomResourceSize(ctx, path_hash, path, resource_size);

return dmResource::RESULT_RESOURCE_NOT_FOUND;
}

Expand Down Expand Up @@ -395,6 +440,10 @@ dmResource::Result ReadResource(HContext ctx, dmhash_t path_hash, const char* pa
}
return ProviderResultToResult(result);
}

if (!ctx->m_CustomFiles.Empty())
return ReadCustomResource(ctx, path_hash, buffer, buffer_size);

return dmResource::RESULT_RESOURCE_NOT_FOUND;
}

Expand All @@ -420,9 +469,60 @@ dmResource::Result ReadResource(HContext ctx, const char* path, dmhash_t path_ha
return ProviderResultToResult(result);
}
}

if (!ctx->m_CustomFiles.Empty())
{
dmResource::Result result = GetCustomResourceSize(ctx, path_hash, path, &resource_size);
if (dmResource::RESULT_OK == result)
{
if (buffer->Capacity() < resource_size)
buffer->SetCapacity(resource_size);
buffer->SetSize(resource_size);

return ReadCustomResource(ctx, path_hash, (uint8_t*)buffer->Begin(), resource_size);
}
}

return dmResource::RESULT_RESOURCE_NOT_FOUND;
}

// ****************************************
// Custom files

dmResource::Result AddFile(HContext context, dmhash_t path_hash, uint32_t size, const void* resource)
{
DM_MUTEX_SCOPED_LOCK(context->m_Mutex);

CustomFile* prevfile = context->m_CustomFiles.Get(path_hash);
if (prevfile)
return dmResource::RESULT_ALREADY_REGISTERED;

CustomFile file;
file.m_Size = size;
file.m_Resource = resource;
if (context->m_CustomFiles.Full())
{
uint32_t capacity = context->m_CustomFiles.Capacity() + 8;
context->m_CustomFiles.SetCapacity((capacity*2)/3, capacity);
}
context->m_CustomFiles.Put(path_hash, file);
return dmResource::RESULT_OK;
}

dmResource::Result RemoveFile(HContext context, dmhash_t path_hash)
{
DM_MUTEX_SCOPED_LOCK(context->m_Mutex);
CustomFile* file = context->m_CustomFiles.Get(path_hash);
if (!file)
return dmResource::RESULT_RESOURCE_NOT_FOUND;

context->m_CustomFiles.Erase(path_hash);
return dmResource::RESULT_OK;
}

// ****************************************


struct DependencyIterContext
{
dmHashTable64<bool> m_Visited;
Expand Down Expand Up @@ -544,4 +644,3 @@ dmResource::Result GetDependencies(HContext ctx, const SGetDependenciesParams* r
}

#undef DM_HASH_FMT

5 changes: 5 additions & 0 deletions engine/resource/src/resource_mounts.h
Expand Up @@ -64,6 +64,11 @@ namespace dmResourceMounts
dmResource::Result GetMountByName(HContext ctx, const char* name, SGetMountResult* mount_info);
dmResource::Result GetMountByIndex(HContext ctx, uint32_t index, SGetMountResult* mount_info);

// See notes in dmsdk/resource/resource.h
// Ownership of memory is with the caller
dmResource::Result AddFile(HContext context, dmhash_t path_hash, uint32_t size, const void* resource);
dmResource::Result RemoveFile(HContext context, dmhash_t path_hash);

struct SGetDependenciesParams
{
dmhash_t m_UrlHash; // The requested url
Expand Down
60 changes: 53 additions & 7 deletions engine/resource/src/test/test_provider_multi.cpp
Expand Up @@ -190,20 +190,20 @@ TEST_F(ArchiveProvidersMulti, ReadFile)
"/archive_data/liveupdate.file7.adc", // exists in zip archive, but overridden in file mout
};

uint32_t file_override_sizes[] = {
0,
0,
13,
20,
25,
bool file_overrides[] = {
false,
false,
true,
true,
true,
};


for (uint32_t i = 0; i < DM_ARRAY_SIZE(file_paths); ++i)
{
dmhash_t path_hash = dmHashString64(file_paths[i]);
uint32_t raw_file_size = 0;
uint8_t* raw_file = GetRawFile(file_paths[i], &raw_file_size, file_override_sizes[i] != 0);
uint8_t* raw_file = GetRawFile(file_paths[i], &raw_file_size, file_overrides[i]);
ASSERT_NE(0U, raw_file_size);
ASSERT_NE((uint8_t*)0, raw_file);

Expand All @@ -225,6 +225,52 @@ TEST_F(ArchiveProvidersMulti, ReadFile)
}
}

TEST_F(ArchiveProvidersMulti, ReadCustomFile)
{
uint8_t file0_data[] = {0,1,2,3,4,5,6,7,8,9};
uint32_t file0_size = DM_ARRAY_SIZE(file0_data);
const char* file0_path = "/custom/test.adc";
dmhash_t file0_hash = dmHashString64(file0_path);

uint32_t resource_size = 0;
dmResource::Result result = dmResource::RESULT_UNKNOWN_ERROR;

// Test without having added the file
result = dmResourceMounts::GetResourceSize(m_Mounts, file0_hash, file0_path, &resource_size);
ASSERT_EQ(dmResource::RESULT_RESOURCE_NOT_FOUND, result);
ASSERT_EQ(0u, resource_size);

// Test with adding the file
result = dmResourceMounts::AddFile(m_Mounts, file0_hash, file0_size, file0_data);
ASSERT_EQ(dmResource::RESULT_OK, result);

result = dmResourceMounts::AddFile(m_Mounts, file0_hash, file0_size, file0_data);
ASSERT_EQ(dmResource::RESULT_ALREADY_REGISTERED, result);

result = dmResourceMounts::GetResourceSize(m_Mounts, file0_hash, file0_path, &resource_size);
ASSERT_EQ(dmResource::RESULT_OK, result);
ASSERT_EQ(file0_size, resource_size);

uint8_t* resource = new uint8_t[resource_size];
result = dmResourceMounts::ReadResource(m_Mounts, file0_hash, file0_path, resource, resource_size);

ASSERT_ARRAY_EQ_LEN(file0_data, resource, resource_size);

delete[] resource;

// Test removing the file
result = dmResourceMounts::RemoveFile(m_Mounts, file0_hash);
ASSERT_EQ(dmResource::RESULT_OK, result);

result = dmResourceMounts::RemoveFile(m_Mounts, file0_hash);
ASSERT_EQ(dmResource::RESULT_RESOURCE_NOT_FOUND, result);

resource_size = 0;
result = dmResourceMounts::GetResourceSize(m_Mounts, file0_hash, file0_path, &resource_size);
ASSERT_EQ(dmResource::RESULT_RESOURCE_NOT_FOUND, result);
ASSERT_EQ(0u, resource_size);
}

int main(int argc, char **argv)
{
dmHashEnableReverseHash(true);
Expand Down