diff --git a/MapBuilder/FileExist.cpp b/MapBuilder/FileExist.cpp index 5743065..28b3ce3 100644 --- a/MapBuilder/FileExist.cpp +++ b/MapBuilder/FileExist.cpp @@ -14,10 +14,26 @@ bool map_files_exist(const std::string& outputPath, const std::string& mapName) return fs::exists(outputPath + "/" + mapName + ".map"); } +bool wow_exist_at_root(const std::string& rootPath) { + return fs::exists(rootPath + "/" + "WoW.exe"); +} + } // namespace file_exist +namespace dir_exist { + +bool output_dir_exist(const std::string& outputPath) { + return fs::exists(outputPath); +} + +} // namespace dir_exist + namespace files { +void create_output_directory(const std::string& outputPath) { + fs::create_directory(outputPath); +} + void create_bvh_output_directory(const std::filesystem::path& outputPath) { fs::create_directories(outputPath / "BVH"); } @@ -30,4 +46,13 @@ void create_nav_output_directory_for_map(const std::filesystem::path& outputPath fs::create_directories(outputPath / "Nav" / mapName); } +std::vectorget_directories(const std::filesystem::path& mapsPath) +{ + std::vector r; + for (auto& p : std::filesystem::recursive_directory_iterator(mapsPath)) + if (p.is_directory()) + r.push_back(p.path().string()); + return r; +} + } // namespace files diff --git a/MapBuilder/FileExist.hpp b/MapBuilder/FileExist.hpp index 4c9a137..adfba54 100644 --- a/MapBuilder/FileExist.hpp +++ b/MapBuilder/FileExist.hpp @@ -1,5 +1,6 @@ #include #include +#include namespace file_exist { @@ -7,14 +8,26 @@ bool bvh_files_exist(const std::string& outputPath); bool map_files_exist(const std::string& outputPath, const std::string& mapName); +bool wow_exist_at_root(const std::string& rootPath); + } // namespace file_exist +namespace dir_exist { + +bool output_dir_exist(const std::string& rootPath); + +} // namespace dir_exist + namespace files { +void create_output_directory(const std::string& rootPath); + void create_bvh_output_directory(const std::filesystem::path& outputPath); void create_nav_output_directory(const std::filesystem::path& outputPath); void create_nav_output_directory_for_map(const std::filesystem::path& outputPath, const std::string& mapName); +std::vector get_directories(const std::filesystem::path& mapsPath); + } // namespace files diff --git a/MapBuilder/main.cpp b/MapBuilder/main.cpp index ab7cd58..c2849b0 100644 --- a/MapBuilder/main.cpp +++ b/MapBuilder/main.cpp @@ -21,6 +21,7 @@ namespace { + void DisplayUsage(std::ostream& o) { o << "Usage:\n"; @@ -36,6 +37,7 @@ void DisplayUsage(std::ostream& o) o << " -t/--threads -- How many worker threads to use\n"; o << " -l/--logLevel -- Log level (0 = none, 1 = " "progress, 2 = warning, 3 = error)\n"; + o << " -a/--alpha -- Automatically extract everything available for alpha client.\n"; #ifdef _DEBUG o << " -x/--adtX -- X coordinate of individual ADT " "to process\n"; @@ -44,166 +46,88 @@ void DisplayUsage(std::ostream& o) #endif o.flush(); } -} // namespace -int main(int argc, char* argv[]) +int ExtractBVH(const std::string& goCSVPath, const std::string& dataPath, + const std::string& outputPath, int threads) { - std::string dataPath, map, outputPath, goCSVPath; - int adtX = -1, adtY = -1, threads = 1, logLevel; - bool bvh = false; - try { - for (auto i = 1; i < argc; ++i) - { - const std::string arg = utility::lower(argv[i]); - const bool lastArgument = i == argc - 1; - - if (arg == "-b" || arg == "--bvh") - { - bvh = true; - continue; - } - else if (arg == "-h" || arg == "--help") - { - // when this is requested, don't do anything else - DisplayUsage(std::cout); - return EXIT_SUCCESS; - } - - if (lastArgument) - throw std::invalid_argument("Missing argument to parameter " + - arg); - - // below here we know that there is another argument - if (arg == "-d" || arg == "--data") - dataPath = argv[++i]; - else if (arg == "-m" || arg == "--map") - map = argv[++i]; - else if (arg == "-g" || arg == "--gocsv") - goCSVPath = argv[++i]; - else if (arg == "-o" || arg == "--output") - outputPath = argv[++i]; - else if (arg == "-t" || arg == "--threads") - threads = std::stoi(argv[++i]); - else if (arg == "-l" || arg == "--loglevel") - logLevel = std::stoi(argv[++i]); -#ifdef _DEBUG - else if (arg == "-x" || arg == "--adtX") - adtX = std::stoi(argv[++i]); - else if (arg == "-y" || arg == "--adtY") - adtY = std::stoi(argv[++i]); -#endif - else - throw std::invalid_argument("Unrecognized argument " + arg); - } + files::create_bvh_output_directory(outputPath); } - catch (std::invalid_argument const& e) + catch (std::exception const& e) // Access denied. { - std::cerr << "ERROR: " << e.what() << std::endl << std::endl; - DisplayUsage(std::cerr); + std::cerr << "ERROR:" << e.what() << std::endl; return EXIT_FAILURE; } - if (outputPath.empty()) - { - std::cerr << "ERROR: Must specify output path" << std::endl; - DisplayUsage(std::cerr); - return EXIT_FAILURE; - } + auto lastStatus = static_cast(0); - if (!bvh && map.empty()) + if (!goCSVPath.empty()) { - std::cerr << "ERROR: Must specify either a map to generate (--map) or " - "the global generation of BVH data (--bvh)" + std::cerr << "ERROR: Specifying gameobject data for BVH " + "generation is meaningless" << std::endl; DisplayUsage(std::cerr); return EXIT_FAILURE; } - if (adtX >= MeshSettings::Adts || adtY >= MeshSettings::Adts) - { - std::cerr << "ERROR: Invalid ADT (" << adtX << ", " << adtY << ")" - << std::endl; - DisplayUsage(std::cerr); - return EXIT_FAILURE; - } + parser::GameObjectBVHBuilder goBuilder(dataPath, outputPath, threads); - auto lastStatus = static_cast(0); + auto const startSize = goBuilder.Remaining(); - std::unique_ptr builder; - std::vector> workers; + goBuilder.Begin(); - try + do { - files::create_bvh_output_directory(outputPath); - - if (bvh) - { - if (!goCSVPath.empty()) - { - std::cerr << "ERROR: Specifying gameobject data for BVH " - "generation is meaningless" - << std::endl; - DisplayUsage(std::cerr); - return EXIT_FAILURE; - } - - parser::GameObjectBVHBuilder goBuilder(dataPath, outputPath, - threads); - - auto const startSize = goBuilder.Remaining(); - - goBuilder.Begin(); - - do - { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - auto const now = time(nullptr); - - auto const finished = goBuilder.Remaining() == 0; - auto const update_status = - finished || now - lastStatus >= STATUS_INTERVAL_SECONDS; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); - // periodically output current status - if (update_status) - { - std::stringstream str; + auto const now = time(nullptr); - auto const completed = startSize - goBuilder.Remaining(); + auto const finished = goBuilder.Remaining() == 0; + auto const update_status = + finished || now - lastStatus >= STATUS_INTERVAL_SECONDS; - auto const percentComplete = - 100.f * static_cast(completed) / startSize; - str << "% Complete: " << std::setprecision(4) - << percentComplete << " (" << std::dec << completed - << " / " << startSize << ")\n"; - std::cout << str.str(); - std::cout.flush(); + // periodically output current status + if (update_status) + { + std::stringstream str; - lastStatus = now; - } + auto const completed = startSize - goBuilder.Remaining(); - if (finished) - break; - } while (true); + auto const percentComplete = + 100.f * static_cast(completed) / startSize; + str << "% Complete: " << std::setprecision(4) << percentComplete + << " (" << std::dec << completed << " / " << startSize << ")\n"; + std::cout << str.str(); + std::cout.flush(); - goBuilder.Shutdown(); + lastStatus = now; + } - std::stringstream fin; - fin << "Finished BVH generation"; - std::cout << fin.str() << std::endl; + if (finished) + break; + } while (true); - return EXIT_SUCCESS; - } + goBuilder.Shutdown(); - files::create_nav_output_directory(outputPath); + std::stringstream fin; + fin << "Finished BVH generation"; + std::cout << fin.str() << std::endl; - // nav mesh generation requires that the MPQ manager be initialized for - // the main thread, whereas BVH generation above does not. + return EXIT_SUCCESS; +} - parser::sMpqManager.Initialize(dataPath); +int ExtractMap(const std::string& map, const std::string& dataPath, + const std::string& outputPath, + const std::string& goCSVPath, int threads, + int adtX, int adtY, int logLevel) +{ + auto lastStatus = static_cast(0); + std::unique_ptr builder; + std::vector> workers; + try + { if (adtX >= 0 && adtY >= 0) { builder = std::make_unique(outputPath, map, logLevel, @@ -282,11 +206,191 @@ int main(int argc, char* argv[]) } builder->SaveMap(); - auto const runTime = time(nullptr) - start; std::cout << "Finished " << map << " (" << builder->CompletedTiles() << " tiles) in " << runTime << " seconds." << std::endl; return EXIT_SUCCESS; +} + +int ExtractAlphaData(std::string& dataPath, std::string& outputPath, + const std::string& goCSVPath, int threads, int logLevel) +{ + if (dataPath.empty()) + dataPath = "./Data"; + + if (outputPath.empty()) + outputPath = "./Output"; + + const std::filesystem::path root(dataPath); + const std::string rootStr = root.parent_path().string(); + if (!file_exist::wow_exist_at_root(rootStr)) + { + std::cerr + << "ERROR: MapBuilder must be placed inside the game root folder" + << std::endl; + return EXIT_FAILURE; + } + + const std::filesystem::path rootOutputPath(outputPath); + const std::string rootOutputPathStr = rootOutputPath.string(); + try + { + if (!dir_exist::output_dir_exist(rootOutputPathStr)) + files::create_output_directory(rootOutputPathStr); + } + catch (std::exception const& e) // Access denied. + { + std::cerr << "ERROR:" << e.what() << std::endl; + return EXIT_FAILURE; + } + + if (ExtractBVH(goCSVPath, dataPath, outputPath, threads) == EXIT_FAILURE) + return EXIT_FAILURE; + + files::create_nav_output_directory(outputPath); + + // nav mesh generation requires that the MPQ manager be initialized for + // the main thread, whereas BVH generation does not. + parser::sMpqManager.Initialize(dataPath); + + std::string map; + std::vector availableMaps = files::get_directories(dataPath + "/World/Maps/"); + for (auto& element : availableMaps) + { + try + { + std::filesystem::path path(element); + map = path.filename().string(); + std::cout << "Building navs for map: " << map << "\n"; + std::cout.flush(); + ExtractMap(path.filename().string(), dataPath, outputPath, + goCSVPath, threads, -1, -1, logLevel); + } + catch (std::exception const& e) + { + std::cerr << "Map: " << map << ", extraction failed : " << e.what() + << std::endl; + } + } + + return EXIT_SUCCESS; +} + +} // namespace + +int main(int argc, char* argv[]) +{ + std::string dataPath, map, outputPath, goCSVPath; + int adtX = -1, adtY = -1, threads = 1, logLevel = 1; + bool bvh = false; + bool alpha = true; + + try + { + for (auto i = 1; i < argc; ++i) + { + const std::string arg = utility::lower(argv[i]); + const bool lastArgument = i == argc - 1; + + if (arg == "-h" || arg == "--help") + { + // when this is requested, don't do anything else + DisplayUsage(std::cout); + return EXIT_SUCCESS; + } + else if (arg == "-a" || arg == "--alpha") + { + alpha = true; + continue; + } + else if (arg == "-b" || arg == "--bvh") + { + bvh = true; + continue; + } + + if (lastArgument) + throw std::invalid_argument("Missing argument to parameter " + + arg); + + // below here we know that there is another argument + if (arg == "-d" || arg == "--data") + dataPath = argv[++i]; + else if (arg == "-m" || arg == "--map") + map = argv[++i]; + else if (arg == "-g" || arg == "--gocsv") + goCSVPath = argv[++i]; + else if (arg == "-o" || arg == "--output") + outputPath = argv[++i]; + else if (arg == "-t" || arg == "--threads") + threads = std::stoi(argv[++i]); + else if (arg == "-l" || arg == "--loglevel") + logLevel = std::stoi(argv[++i]); +#ifdef _DEBUG + else if (arg == "-x" || arg == "--adtX") + adtX = std::stoi(argv[++i]); + else if (arg == "-y" || arg == "--adtY") + adtY = std::stoi(argv[++i]); +#endif + else + throw std::invalid_argument("Unrecognized argument " + arg); + } + } + catch (std::invalid_argument const& e) + { + std::cerr << "ERROR: " << e.what() << std::endl << std::endl; + DisplayUsage(std::cerr); + return EXIT_FAILURE; + } + + // Alpha: Extract BVH and everything inside World/Maps. + if (alpha) + { + return ExtractAlphaData(dataPath, outputPath, goCSVPath, threads, logLevel); + } + + if (outputPath.empty()) + { + std::cerr << "ERROR: Must specify output path" << std::endl; + DisplayUsage(std::cerr); + return EXIT_FAILURE; + } + + if (!bvh && map.empty()) + { + std::cerr << "ERROR: Must specify either a map to generate (--map) or " + "the global generation of BVH data (--bvh)" + << std::endl; + DisplayUsage(std::cerr); + return EXIT_FAILURE; + } + + if (adtX >= MeshSettings::Adts || adtY >= MeshSettings::Adts) + { + std::cerr << "ERROR: Invalid ADT (" << adtX << ", " << adtY << ")" + << std::endl; + DisplayUsage(std::cerr); + return EXIT_FAILURE; + } + + if (bvh) + return ExtractBVH(goCSVPath, dataPath, outputPath, threads); + + try + { + files::create_nav_output_directory(outputPath); + } + catch (std::exception const& e) // Access denied. + { + std::cerr << "ERROR:" << e.what() << std::endl; + return EXIT_FAILURE; + } + + // nav mesh generation requires that the MPQ manager be initialized for + // the main thread, whereas BVH generation does not. + parser::sMpqManager.Initialize(dataPath); + + return ExtractMap(map, dataPath, outputPath, goCSVPath, threads, adtX, adtY, logLevel); } \ No newline at end of file diff --git a/MapViewer/main.cpp b/MapViewer/main.cpp index 75bc4c5..bbaed7f 100644 --- a/MapViewer/main.cpp +++ b/MapViewer/main.cpp @@ -875,15 +875,24 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, maps.emplace_back("000 Azeroth"); maps.emplace_back("001 Kalimdor"); maps.emplace_back("013 Test"); + maps.emplace_back("017 Kalidar"); maps.emplace_back("025 Scott Test"); maps.emplace_back("029 Test"); maps.emplace_back("030 PVPZone01 (Alterac Valley)"); maps.emplace_back("033 Shadowfang"); maps.emplace_back("034 StormwindJail (Stockades)"); + maps.emplace_back("035 StormwindPrison"); maps.emplace_back("036 DeadminesInstance"); maps.emplace_back("037 PVPZone02 (Azshara Crater)"); + maps.emplace_back("042 Collin"); maps.emplace_back("043 WailingCaverns"); + maps.emplace_back("044 Monastery"); + maps.emplace_back("047 RazorfenKraulInstance"); + maps.emplace_back("048 Blackfathom"); + maps.emplace_back("070 Uldaman"); maps.emplace_back("090 GnomeragonInstance"); + maps.emplace_back("109 SunkenTemple"); + maps.emplace_back("129 RazorfenDowns"); maps.emplace_back("229 BlackrockSpire"); maps.emplace_back("429 DireMaul"); maps.emplace_back("451 Development");