diff --git a/include/config.h b/include/config.h index f37a90e..8b5ba03 100644 --- a/include/config.h +++ b/include/config.h @@ -47,6 +47,7 @@ struct DownloadConfig bool bSaveChangelogs; bool bSaveSerials; bool bAutomaticXMLCreation; + bool bFreeSpaceCheck; bool bInstallers; bool bExtras; diff --git a/include/util.h b/include/util.h index 7baf665..1c45ed5 100644 --- a/include/util.h +++ b/include/util.h @@ -92,6 +92,7 @@ namespace Util curl_off_t CurlWriteMemoryCallback(char *ptr, curl_off_t size, curl_off_t nmemb, void *userp); curl_off_t CurlWriteChunkMemoryCallback(void *contents, curl_off_t size, curl_off_t nmemb, void *userp); curl_off_t CurlReadChunkMemoryCallback(void *contents, curl_off_t size, curl_off_t nmemb, ChunkMemoryStruct *userp); + std::string makeSizeString(const unsigned long long& iSizeInBytes); template std::string formattedString(const std::string& format, Args ... args) { diff --git a/main.cpp b/main.cpp index 74805a8..f1b70b5 100644 --- a/main.cpp +++ b/main.cpp @@ -290,6 +290,7 @@ int main(int argc, char *argv[]) ("include-hidden-products", bpo::value(&Globals::globalConfig.bIncludeHiddenProducts)->zero_tokens()->default_value(false), "Include games that have been set hidden in account page") ("size-only", bpo::value(&Globals::globalConfig.bSizeOnly)->zero_tokens()->default_value(false), "Don't check the hashes of the files whose size matches that on the server") ("verbosity", bpo::value(&Globals::globalConfig.iMsgLevel)->default_value(0), "Set message verbosity level\n -1 = Less verbose\n 0 = Default\n 1 = Verbose\n 2 = Debug") + ("check-free-space", bpo::value(&Globals::globalConfig.dlConf.bFreeSpaceCheck)->zero_tokens()->default_value(false), "Check for available free space before starting download") ; options_cli_no_cfg_hidden.add_options() diff --git a/src/downloader.cpp b/src/downloader.cpp index 0897883..0ae3d35 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -735,6 +735,30 @@ void Downloader::download() if (!dlQueue.empty()) { + unsigned long long totalSizeBytes = iTotalRemainingBytes.load(); + std::cout << "Total size: " << Util::makeSizeString(totalSizeBytes) << std::endl; + + if (Globals::globalConfig.dlConf.bFreeSpaceCheck) + { + boost::filesystem::path path = boost::filesystem::absolute(Globals::globalConfig.dirConf.sDirectory); + while(!boost::filesystem::exists(path) && !path.empty()) + { + path = path.parent_path(); + } + + if(boost::filesystem::exists(path) && !path.empty()) + { + boost::filesystem::space_info space = boost::filesystem::space(path); + + if (space.available < totalSizeBytes) + { + std::cerr << "Not enough free space in " << boost::filesystem::canonical(path) << " (" + << Util::makeSizeString(space.available) << ")"<< std::endl; + exit(1); + } + } + } + // Limit thread count to number of items in download queue unsigned int iThreads = std::min(Globals::globalConfig.iThreads, static_cast(dlQueue.size())); @@ -3871,10 +3895,30 @@ void Downloader::galaxyInstallGameById(const std::string& product_id, int build_ dlQueueGalaxy.push(items[i]); } - double totalSizeMB = static_cast(totalSize)/1024/1024; std::cout << game_title << std::endl; std::cout << "Files: " << items.size() << std::endl; - std::cout << "Total size installed: " << totalSizeMB << " MB" << std::endl; + std::cout << "Total size installed: " << Util::makeSizeString(totalSize) << std::endl; + + if (Globals::globalConfig.dlConf.bFreeSpaceCheck) + { + boost::filesystem::path path = boost::filesystem::absolute(install_path); + while(!boost::filesystem::exists(path) && !path.empty()) + { + path = path.parent_path(); + } + + if(boost::filesystem::exists(path) && !path.empty()) + { + boost::filesystem::space_info space = boost::filesystem::space(path); + + if (space.available < totalSize) + { + std::cerr << "Not enough free space in " << boost::filesystem::canonical(path) << " (" + << Util::makeSizeString(space.available) << ")"<< std::endl; + exit(1); + } + } + } // Limit thread count to number of items in download queue unsigned int iThreads = std::min(Globals::globalConfig.iThreads, static_cast(dlQueueGalaxy.size())); @@ -5166,19 +5210,6 @@ void Downloader::galaxyInstallGame_MojoSetupHack(const std::string& product_id) } } - // Create directories - for (std::uintmax_t i = 0; i < vZipDirectories.size(); ++i) - { - if (!boost::filesystem::exists(vZipDirectories[i].filepath)) - { - if (!boost::filesystem::create_directories(vZipDirectories[i].filepath)) - { - std::cerr << "Failed to create directory " << vZipDirectories[i].filepath << std::endl; - return; - } - } - } - // Set start and end offsets for split files // Create map of split files for combining them later splitFilesMap mSplitFiles; @@ -5216,10 +5247,12 @@ void Downloader::galaxyInstallGame_MojoSetupHack(const std::string& product_id) } // Add files to download queue + uintmax_t totalSize = 0; for (std::uintmax_t i = 0; i < vZipFiles.size(); ++i) { dlQueueGalaxy_MojoSetupHack.push(vZipFiles[i]); iTotalRemainingBytes.fetch_add(vZipFiles[i].comp_size); + totalSize += vZipFiles[i].uncomp_size; } // Add symlinks to download queue @@ -5227,6 +5260,45 @@ void Downloader::galaxyInstallGame_MojoSetupHack(const std::string& product_id) { dlQueueGalaxy_MojoSetupHack.push(vZipFilesSymlink[i]); iTotalRemainingBytes.fetch_add(vZipFilesSymlink[i].comp_size); + totalSize += vZipFilesSymlink[i].uncomp_size; + } + + std::cout << game.title << std::endl; + std::cout << "Files: " << dlQueueGalaxy_MojoSetupHack.size() << std::endl; + std::cout << "Total size installed: " << Util::makeSizeString(totalSize) << std::endl; + + if (Globals::globalConfig.dlConf.bFreeSpaceCheck) + { + boost::filesystem::path path = boost::filesystem::absolute(install_path); + while(!boost::filesystem::exists(path) && !path.empty()) + { + path = path.parent_path(); + } + + if(boost::filesystem::exists(path) && !path.empty()) + { + boost::filesystem::space_info space = boost::filesystem::space(path); + + if (space.available < totalSize) + { + std::cerr << "Not enough free space in " << boost::filesystem::canonical(path) << " (" + << Util::makeSizeString(space.available) << ")"<< std::endl; + exit(1); + } + } + } + + // Create directories + for (std::uintmax_t i = 0; i < vZipDirectories.size(); ++i) + { + if (!boost::filesystem::exists(vZipDirectories[i].filepath)) + { + if (!boost::filesystem::create_directories(vZipDirectories[i].filepath)) + { + std::cerr << "Failed to create directory " << vZipDirectories[i].filepath << std::endl; + return; + } + } } // Limit thread count to number of items in download queue diff --git a/src/util.cpp b/src/util.cpp index 5ff61d9..3228f92 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -893,3 +893,19 @@ curl_off_t Util::CurlReadChunkMemoryCallback(void *contents, curl_off_t size, cu return realsize; } + +std::string Util::makeSizeString(const unsigned long long& iSizeInBytes) +{ + auto units = { "B", "kB", "MB", "GB", "TB", "PB" }; + std::string size_unit = "B"; + double iSize = static_cast(iSizeInBytes); + for (auto unit : units) + { + size_unit = unit; + if (iSize < 1024) + break; + + iSize /= 1024; + } + return formattedString("%0.2f %s", iSize, size_unit.c_str()); +}