#include "pch.h" #include "log.h" #include "asset.h" #include "platform_api/network_tls_policy.h" #ifdef __APPLE__ #include #include "objc_utils.h" #endif #ifdef __ANDROID__ #include #include AAssetManager* Asset::m_am; bool android_create_dir(const std::string& path); void Asset::set_android_asset_manager(AAssetManager* asset_manager) { m_am = 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 Asset::list_files(std::string folder, const std::string& filter_regex) { bool is_asset = Asset::is_asset(folder); std::vector 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(Asset::m_am, 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*>(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(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 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& Asset::open_url_async(const std::string& url, std::function progress /*= nullptr*/, std::function 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_asset = AAssetManager_open(m_am, 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(m_asset); m_data = (uint8_t*)AAsset_getBuffer(m_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_asset) AAsset_close(m_asset); m_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(); }