364 lines
9.0 KiB
C++
364 lines
9.0 KiB
C++
#include "pch.h"
|
|
#include "log.h"
|
|
#include "asset.h"
|
|
#include "platform_api/network_tls_policy.h"
|
|
|
|
#ifdef __APPLE__
|
|
#include <Foundation/Foundation.h>
|
|
#include "objc_utils.h"
|
|
#endif
|
|
|
|
#ifdef __ANDROID__
|
|
#include <android/asset_manager.h>
|
|
#include <dirent.h>
|
|
void* Asset::m_android_asset_manager;
|
|
bool android_create_dir(const std::string& path);
|
|
|
|
namespace {
|
|
|
|
[[nodiscard]] AAsset* android_asset_handle(void* asset)
|
|
{
|
|
return static_cast<AAsset*>(asset);
|
|
}
|
|
|
|
}
|
|
|
|
void Asset::set_android_asset_manager(void* asset_manager)
|
|
{
|
|
m_android_asset_manager = asset_manager;
|
|
}
|
|
#endif
|
|
|
|
bool Asset::delete_file(const std::string& path)
|
|
{
|
|
LOG("delete file: %s", path.c_str());
|
|
std::remove(path.c_str());
|
|
return true;
|
|
}
|
|
|
|
bool Asset::exist(std::string path)
|
|
{
|
|
if (Asset::is_asset(path))
|
|
{
|
|
Asset asset;
|
|
if (asset.open(path.c_str()))
|
|
{
|
|
asset.close();
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FILE* fp = fopen(path.c_str(), "rb");
|
|
if (!fp)
|
|
return false;
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
return false; // useless return for the stupid xcode
|
|
}
|
|
|
|
std::vector<std::string> Asset::list_files(std::string folder, const std::string& filter_regex)
|
|
{
|
|
bool is_asset = Asset::is_asset(folder);
|
|
std::vector<std::string> names;
|
|
#ifdef _WIN32
|
|
WIN32_FIND_DATAA fd;
|
|
HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &fd);
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
do
|
|
{
|
|
// read all (real) files in current folder
|
|
// , delete '!' read other 2 default folder . and ..
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
names.push_back(fd.cFileName);
|
|
}
|
|
} while (::FindNextFileA(hFind, &fd));
|
|
::FindClose(hFind);
|
|
}
|
|
#elif __ANDROID__
|
|
if (is_asset)
|
|
{
|
|
AAssetDir* dir = AAssetManager_openDir(static_cast<AAssetManager*>(Asset::m_android_asset_manager), folder.c_str());
|
|
while (const char* name = AAssetDir_getNextFileName(dir))
|
|
{
|
|
//LOG("asset: %s", name);
|
|
names.push_back(name);
|
|
}
|
|
AAssetDir_close(dir);
|
|
}
|
|
else
|
|
{
|
|
DIR *dp;
|
|
struct dirent *ep;
|
|
dp = opendir(folder.c_str());
|
|
|
|
if (dp != NULL)
|
|
{
|
|
while ((ep = readdir(dp)))
|
|
if (ep->d_type != DT_DIR)
|
|
names.push_back(ep->d_name);
|
|
closedir(dp);
|
|
}
|
|
else
|
|
LOG("Couldn't open the directory: %s", folder.c_str());
|
|
}
|
|
#else
|
|
std::string abs_path = folder;
|
|
if (is_asset)
|
|
{
|
|
abs_path = absolute(folder);
|
|
}
|
|
|
|
DIR *dp;
|
|
struct dirent *ep;
|
|
dp = opendir(abs_path.c_str());
|
|
|
|
if (dp != NULL)
|
|
{
|
|
while ((ep = readdir(dp)))
|
|
if (ep->d_type != DT_DIR)
|
|
names.push_back(ep->d_name);
|
|
closedir(dp);
|
|
}
|
|
else
|
|
LOG("Couldn't open the directory: %s", folder.c_str());
|
|
#endif
|
|
|
|
if (!filter_regex.empty())
|
|
{
|
|
std::regex r(filter_regex);
|
|
names.erase(std::remove_if(names.begin(), names.end(),
|
|
[&r](const std::string& s) { return !std::regex_match(s, r); }), names.end());
|
|
}
|
|
|
|
return names;
|
|
}
|
|
|
|
std::string Asset::absolute(const std::string& path)
|
|
{
|
|
#ifdef __APPLE__
|
|
NSString* bundle_path = [[NSBundle mainBundle] resourcePath];
|
|
std::string base = [bundle_path cStringUsingEncoding:1];
|
|
return base + "/" + path;
|
|
#else
|
|
return path;
|
|
#endif
|
|
}
|
|
|
|
bool Asset::is_asset(const std::string & path)
|
|
{
|
|
return path.substr(0, 5) == "data/";
|
|
}
|
|
|
|
bool Asset::create_dir(const std::string& path)
|
|
{
|
|
#if __WIN__
|
|
return PathFileExistsA(path.c_str()) ? true : CreateDirectoryA(path.c_str(), NULL);
|
|
#elif __ANDROID__
|
|
return android_create_dir(path);
|
|
#elif __IOS__ || __OSX__
|
|
return apple_create_dir(path);
|
|
#else
|
|
// TODO: implement for linux and web
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static size_t curl_data_handler_asset(void* contents, size_t size, size_t nmemb, void* userp)
|
|
{
|
|
auto buffer = reinterpret_cast<std::vector<uint8_t>*>(userp);
|
|
buffer->insert(buffer->end(), (char*)contents, (char*)contents + (size * nmemb));
|
|
return size * nmemb;
|
|
}
|
|
|
|
#if WITH_CURL
|
|
static int progress_callback_asset_download(void* clientp, curl_off_t dltotal,
|
|
curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
|
{
|
|
Asset* ass = static_cast<Asset*>(clientp);
|
|
if (ass->m_stop_async)
|
|
{
|
|
LOG("interrupt download of %s", ass->m_current_url.c_str());
|
|
return 1;
|
|
}
|
|
float p = dltotal > 0 ? (float)dlnow / (float)dltotal : 0.f;
|
|
bool cont = ass->on_progress(p);
|
|
return cont ? 0 : 1;
|
|
}
|
|
#endif //CURL
|
|
|
|
bool Asset::open_url(const std::string& url, std::function<bool(float)> progress /*= nullptr*/)
|
|
{
|
|
#if WITH_CURL
|
|
CURL* curl = curl_easy_init();
|
|
if (curl)
|
|
{
|
|
close();
|
|
LOG("download %s", url.c_str());
|
|
m_current_url = url;
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &tmp_data);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_data_handler_asset);
|
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
|
if (pp::platform::default_disables_network_tls_verification())
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
|
if (progress)
|
|
{
|
|
on_progress = progress;
|
|
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback_asset_download);
|
|
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this);
|
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
|
}
|
|
auto err = curl_easy_perform(curl);
|
|
curl_easy_cleanup(curl);
|
|
|
|
if (err == CURLE_OK)
|
|
{
|
|
if (m_data)
|
|
delete m_data;
|
|
m_data = new uint8_t[tmp_data.size()];
|
|
std::copy(tmp_data.begin(), tmp_data.end(), m_data);
|
|
m_len = tmp_data.size();
|
|
tmp_data.clear();
|
|
return true;
|
|
}
|
|
}
|
|
#endif // CURL
|
|
return false;
|
|
}
|
|
|
|
std::future<bool>& Asset::open_url_async(const std::string& url,
|
|
std::function<bool(float)> progress /*= nullptr*/, std::function<void(bool)> complete /*= nullptr*/)
|
|
{
|
|
close_remote();
|
|
m_stop_async = false;
|
|
remote_future = std::async([this, url, progress, complete] {
|
|
bool ok = open_url(url, progress);
|
|
if (complete)
|
|
complete(ok);
|
|
return ok;
|
|
});
|
|
return remote_future;
|
|
}
|
|
|
|
bool Asset::open(const char* path)
|
|
{
|
|
//LOG("Asset::open %s", path);
|
|
m_current_path = path;
|
|
std::string file_path = path;
|
|
#ifdef __ANDROID__
|
|
if (is_asset(path))
|
|
{
|
|
if (!(m_android_asset = AAssetManager_open(
|
|
static_cast<AAssetManager*>(m_android_asset_manager),
|
|
path,
|
|
AASSET_MODE_RANDOM)))
|
|
{
|
|
LOG("AAssetManager_open failed %s", path);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(m_fp = fopen(file_path.c_str(), "rb")))
|
|
{
|
|
LOG("asset open errno = %d, %s", errno, path);
|
|
return false;
|
|
}
|
|
fseek(m_fp, 0, SEEK_END);
|
|
m_len = (int)ftell(m_fp);
|
|
fseek(m_fp, 0, SEEK_SET);
|
|
}
|
|
#else
|
|
#ifdef __APPLE__
|
|
if (is_asset(path))
|
|
{
|
|
NSString* bundle_path = [[NSBundle mainBundle] resourcePath];
|
|
std::string base = [bundle_path cStringUsingEncoding:1];
|
|
file_path = base + "/" + path;
|
|
}
|
|
#endif
|
|
//LOG("asset file: %s", file_path.c_str());
|
|
if (!(m_fp = fopen(file_path.c_str(), "rb")))
|
|
{
|
|
LOG("asset open errno = %d, %s", errno, path);
|
|
return false;
|
|
}
|
|
fseek(m_fp, 0, SEEK_END);
|
|
m_len = (int)ftell(m_fp);
|
|
fseek(m_fp, 0, SEEK_SET);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
glm::uint8_t* Asset::read_all()
|
|
{
|
|
#ifdef __ANDROID__
|
|
if (!m_data)
|
|
{
|
|
if (is_asset(m_current_path))
|
|
{
|
|
m_len = (int)AAsset_getLength(android_asset_handle(m_android_asset));
|
|
m_data = (uint8_t*)AAsset_getBuffer(android_asset_handle(m_android_asset));
|
|
}
|
|
else
|
|
{
|
|
m_data = new uint8_t[m_len];
|
|
if (m_len != fread(m_data, 1, m_len, m_fp))
|
|
{
|
|
LOG("ASSET READ FAILED for %s", m_current_path.c_str());
|
|
delete[] m_data;
|
|
m_data = nullptr;
|
|
}
|
|
}
|
|
}
|
|
return m_data;
|
|
#else
|
|
if (!m_data)
|
|
{
|
|
m_data = new uint8_t[m_len];
|
|
if (m_len != fread(m_data, 1, m_len, m_fp))
|
|
{
|
|
LOG("ASSET READ FAILED for %s", m_current_path.c_str());
|
|
delete[] m_data;
|
|
m_data = nullptr;
|
|
}
|
|
}
|
|
return m_data;
|
|
#endif
|
|
}
|
|
|
|
void Asset::close()
|
|
{
|
|
#ifdef __ANDROID__
|
|
if (m_android_asset)
|
|
AAsset_close(android_asset_handle(m_android_asset));
|
|
m_android_asset = nullptr;
|
|
#else
|
|
if (m_fp)
|
|
fclose(m_fp);
|
|
if (m_data)
|
|
delete m_data;
|
|
m_data = nullptr;
|
|
m_fp = nullptr;
|
|
#endif
|
|
}
|
|
|
|
void Asset::close_remote()
|
|
{
|
|
if (remote_future.valid())
|
|
{
|
|
m_stop_async = true;
|
|
remote_future.get();
|
|
}
|
|
}
|
|
|
|
Asset::~Asset()
|
|
{
|
|
close_remote();
|
|
close();
|
|
}
|