From 2bcdb36df1a9792085961d9fb762c7f7e0945310 Mon Sep 17 00:00:00 2001 From: milek7 Date: Wed, 21 Aug 2019 17:18:39 +0200 Subject: [PATCH] further work on launcher --- launcher/deferred_image.h | 48 +++++++++++++ launcher/launcheruilayer.cpp | 5 ++ launcher/launcheruilayer.h | 5 ++ launcher/scenery_list.cpp | 126 ++++++++++++++++++++++++++++++---- launcher/scenery_list.h | 8 ++- launcher/scenery_scanner.cpp | 52 +++++++++++--- launcher/scenery_scanner.h | 13 ++-- launcher/textures_scanner.cpp | 35 +++++++--- launcher/textures_scanner.h | 8 ++- launcher/vehicle_picker.cpp | 58 +++------------- scene.h | 1 + translation.h | 2 +- utilities.cpp | 6 +- 13 files changed, 269 insertions(+), 98 deletions(-) create mode 100644 launcher/deferred_image.h diff --git a/launcher/deferred_image.h b/launcher/deferred_image.h new file mode 100644 index 00000000..10f557fc --- /dev/null +++ b/launcher/deferred_image.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Texture.h" +#include "renderer.h" + +class deferred_image { +public: + deferred_image() = default; + deferred_image(const std::string &p) : path(p) { } + deferred_image(const deferred_image&) = delete; + deferred_image(deferred_image&&) = default; + deferred_image &operator=(deferred_image&&) = default; + operator bool() const + { + return image != null_handle || !path.empty(); + } + + GLuint get() const + { + if (!path.empty()) { + image = GfxRenderer.Fetch_Texture(path, true); + path.clear(); + } + + if (image != null_handle) { + opengl_texture &tex = GfxRenderer.Texture(image); + tex.create(); + + if (tex.is_ready) + return tex.id; + } + + return -1; + } + + glm::ivec2 size() const + { + if (image != null_handle) { + opengl_texture &tex = GfxRenderer.Texture(image); + return glm::ivec2(tex.width(), tex.height()); + } + return glm::ivec2(); + } + +private: + mutable std::string path; + mutable texture_handle image = 0; +}; diff --git a/launcher/launcheruilayer.cpp b/launcher/launcheruilayer.cpp index 6ca93e9b..18069cca 100644 --- a/launcher/launcheruilayer.cpp +++ b/launcher/launcheruilayer.cpp @@ -2,8 +2,13 @@ #include "launcher/launcheruilayer.h" #include "application.h" +#include "Logs.h" launcher_ui::launcher_ui() + : m_scenery_scanner(m_vehicles_bank), m_scenerylist_panel(m_scenery_scanner) { + m_vehicles_bank.scan_textures(); + m_scenery_scanner.scan(); + add_external_panel(&m_scenerylist_panel); add_external_panel(&m_keymapper_panel); add_external_panel(&m_vehiclepicker_panel); diff --git a/launcher/launcheruilayer.h b/launcher/launcheruilayer.h index a38b4492..9d8cf50d 100644 --- a/launcher/launcheruilayer.h +++ b/launcher/launcheruilayer.h @@ -13,6 +13,8 @@ http://mozilla.org/MPL/2.0/. #include "launcher/scenery_list.h" #include "launcher/keymapper.h" #include "launcher/vehicle_picker.h" +#include "launcher/textures_scanner.h" +#include "launcher/scenery_scanner.h" class launcher_ui : public ui_layer { public: @@ -22,6 +24,9 @@ public: private: void render_() override; + ui::vehicles_bank m_vehicles_bank; + scenery_scanner m_scenery_scanner; + ui::scenerylist_panel m_scenerylist_panel; ui::keymapper_panel m_keymapper_panel; ui::vehiclepicker_panel m_vehiclepicker_panel; diff --git a/launcher/scenery_list.cpp b/launcher/scenery_list.cpp index 7c8d83cb..7c1fbdd1 100644 --- a/launcher/scenery_list.cpp +++ b/launcher/scenery_list.cpp @@ -3,12 +3,12 @@ #include "imgui/imgui.h" #include "utilities.h" #include "renderer.h" +#include "McZapkie/MOVER.h" #include -ui::scenerylist_panel::scenerylist_panel() - : ui_panel(STR("Scenario list"), false) +ui::scenerylist_panel::scenerylist_panel(scenery_scanner &scanner) + : ui_panel(STR("Scenario list"), false), scanner(scanner) { - scanner.scan(); } void ui::scenerylist_panel::render() @@ -21,7 +21,7 @@ void ui::scenerylist_panel::render() if (ImGui::Begin(panelname.c_str(), &is_open)) { ImGui::Columns(3); - if (ImGui::BeginChild("child1", ImVec2(0, -100))) { + if (ImGui::BeginChild("child1", ImVec2(0, -200))) { std::string prev_prefix; bool collapse_open = false; @@ -43,8 +43,10 @@ void ui::scenerylist_panel::render() selected_scenery = &desc; ImGui::Indent(10.0f); - if (ImGui::Selectable(name.c_str(), &desc == selected_scenery)) + if (ImGui::Selectable(name.c_str(), &desc == selected_scenery)) { selected_scenery = &desc; + selected_trainset = nullptr; + } ImGui::Unindent(10.0f); } } @@ -52,7 +54,7 @@ void ui::scenerylist_panel::render() ImGui::NextColumn(); - if (ImGui::BeginChild("child2", ImVec2(0, -100))) { + if (ImGui::BeginChild("child2", ImVec2(0, -200))) { if (selected_scenery) { ImGui::TextWrapped("%s", selected_scenery->name.c_str()); ImGui::TextWrapped("%s", selected_scenery->description.c_str()); @@ -77,7 +79,7 @@ void ui::scenerylist_panel::render() ImGui::NextColumn(); if (selected_scenery) { - if (ImGui::BeginChild("child3", ImVec2(0, -100), false, ImGuiWindowFlags_NoScrollbar)) { + if (ImGui::BeginChild("child3", ImVec2(0, -200), false, ImGuiWindowFlags_NoScrollbar)) { if (!selected_scenery->image_path.empty()) { scenery_desc *desc = const_cast(selected_scenery); desc->image = GfxRenderer.Fetch_Texture(selected_scenery->image_path, true); @@ -102,22 +104,116 @@ void ui::scenerylist_panel::render() if (ImGui::BeginChild("child4")) { if (selected_scenery) { - ImGui::Columns(2); + if (selected_trainset) + ImGui::Columns(2); if (ImGui::BeginChild("child5", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) { - for (auto const &trainset : selected_scenery->trainsets) { - std::string z = trainset.name; - for (auto const &dyn_desc : trainset.vehicles) - z += dyn_desc.name + "+"; - ImGui::Selectable(z.c_str(), false); + ImGuiListClipper clipper(selected_scenery->trainsets.size()); + while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + auto const &trainset = selected_scenery->trainsets[i]; + ImGui::PushID(i); + if (ImGui::Selectable("##set", selected_trainset == &trainset, 0, ImVec2(0, 30))) + selected_trainset = &trainset; + ImGui::SameLine(); + draw_trainset(trainset); + ImGui::NewLine(); + ImGui::PopID(); + //ImGui::Selectable(z.c_str(), false); } } ImGui::EndChild(); - ImGui::NextColumn(); - //ImGui::Button(STR_C("Launch")); + if (selected_trainset) { + ImGui::NextColumn(); + ImGui::TextWrapped(selected_trainset->description.c_str()); + ImGui::Button(STR_C("Launch"), ImVec2(-1, 0)); + } } } ImGui::EndChild(); } ImGui::End(); } + +void ui::scenerylist_panel::draw_trainset(const trainset_desc &trainset) +{ + static std::unordered_map coupling_names = + { + { coupling::faux, STRN("faux") }, + { coupling::coupler, STRN("coupler") }, + { coupling::brakehose, STRN("brake hose") }, + { coupling::control, STRN("control") }, + { coupling::highvoltage, STRN("high voltage") }, + { coupling::gangway, STRN("gangway") }, + { coupling::mainhose, STRN("main hose") }, + { coupling::heating, STRN("heating") }, + { coupling::permanent, STRN("permanent") }, + { coupling::uic, STRN("uic") } + }; + + draw_droptarget(); + ImGui::SameLine(15.0f); + + for (auto const &dyn_desc : trainset.vehicles) { + if (dyn_desc.skin && dyn_desc.skin->mini.get() != -1) { + ImGui::PushID(static_cast(&dyn_desc)); + + glm::ivec2 size = dyn_desc.skin->mini.size(); + float width = 30.0f / size.y * size.x; + ImGui::Image(reinterpret_cast(dyn_desc.skin->mini.get()), ImVec2(width, 30), ImVec2(0, 1), ImVec2(1, 0)); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + std::string name = (dyn_desc.vehicle->path.parent_path() / dyn_desc.vehicle->path.stem()).string(); + std::string skin = dyn_desc.skin->skin; + ImGui::Text(STR_C("ID: %s"), dyn_desc.name.c_str()); + ImGui::NewLine(); + + ImGui::Text(STR_C("Type: %s"), name.c_str()); + ImGui::Text(STR_C("Skin: %s"), skin.c_str()); + ImGui::NewLine(); + + if (dyn_desc.drivertype != "nobody") + ImGui::Text(STR_C("Occupied: %s"), dyn_desc.drivertype.c_str()); + if (dyn_desc.loadcount > 0) + ImGui::Text(STR_C("Load: %s: %d"), dyn_desc.loadtype.c_str(), dyn_desc.loadcount); + if (!dyn_desc.params.empty()) + ImGui::Text(STR_C("Parameters: %s"), dyn_desc.params.c_str()); + ImGui::NewLine(); + + ImGui::TextUnformatted(STR_C("Coupling:")); + + for (int i = 1; i <= 0x100; i <<= 1) { + bool dummy = true; + + if (dyn_desc.coupling & i) { + std::string label = STRN("unknown"); + auto it = coupling_names.find(static_cast(i)); + if (it != coupling_names.end()) + label = it->second; + ImGui::Checkbox(Translations.lookup_c(label.c_str()), &dummy); + } + } + + ImGui::EndTooltip(); + } + + ImGui::SameLine(0, 0); + float originX = ImGui::GetCursorPosX(); + + ImGui::SameLine(originX - 7.5f); + draw_droptarget(); + ImGui::SameLine(originX); + + ImGui::PopID(); + } + } +} + +void ui::scenerylist_panel::draw_droptarget() +{ + ImGui::Dummy(ImVec2(15, 30)); + if (ImGui::BeginDragDropTarget()) { + ImGui::AcceptDragDropPayload("skin"); + ImGui::EndDragDropTarget(); + } +} diff --git a/launcher/scenery_list.h b/launcher/scenery_list.h index da5ee7eb..039e7cce 100644 --- a/launcher/scenery_list.h +++ b/launcher/scenery_list.h @@ -8,14 +8,16 @@ namespace ui class scenerylist_panel : public ui_panel { public: - scenerylist_panel(); + scenerylist_panel(scenery_scanner &scanner); void render() override; private: - scenery_scanner scanner; + scenery_scanner &scanner; scenery_desc const *selected_scenery = nullptr; + trainset_desc const *selected_trainset = nullptr; - vehicles_bank &bank; + void draw_trainset(const trainset_desc &trainset); + void draw_droptarget(); }; } // namespace ui diff --git a/launcher/scenery_scanner.cpp b/launcher/scenery_scanner.cpp index 15cb7281..3b7f6c14 100644 --- a/launcher/scenery_scanner.cpp +++ b/launcher/scenery_scanner.cpp @@ -2,6 +2,12 @@ #include "scenery_scanner.h" #include "Logs.h" +scenery_scanner::scenery_scanner(ui::vehicles_bank &bank) + : bank(bank) +{ + +} + void scenery_scanner::scan() { for (auto &f : std::filesystem::directory_iterator("scenery")) { @@ -70,11 +76,6 @@ void scenery_scanner::scan_scn(std::filesystem::path path) } } } - - WriteLog(path.string() + "----------"); - for (trainset_desc &trainset : desc.trainsets) { - WriteLog(trainset.name + "=" + std::to_string(trainset.file_bounds.first) + ":" + std::to_string(trainset.file_bounds.second) + "@" + trainset.description); - } } void scenery_scanner::parse_trainset(cParser &parser) @@ -92,7 +93,7 @@ void scenery_scanner::parse_trainset(cParser &parser) trainset.vehicles.emplace_back(); dynamic_desc &dyn = trainset.vehicles.back(); - std::string datafolder, skinfile, mmdfile; + std::string datafolder, skinfile, mmdfile, params; int offset; parser.getTokens(2); // range_max, range_min @@ -103,10 +104,43 @@ void scenery_scanner::parse_trainset(cParser &parser) break; parser.getTokens(7, false); - parser >> datafolder >> skinfile >> mmdfile >> offset >> dyn.drivertype >> dyn.coupling >> dyn.loadcount; + parser >> datafolder >> skinfile >> mmdfile >> offset >> dyn.drivertype >> params >> dyn.loadcount; - dyn.skin = datafolder + "/" + skinfile; - dyn.mmd = datafolder + "/" + mmdfile; + size_t params_pos = params.find('.'); + if (params_pos != -1 && params_pos < params.size()) { + dyn.params = params.substr(params_pos + 1, -1); + params.erase(params_pos); + } + + { + std::istringstream couplingparser(params); + couplingparser >> dyn.coupling; + } + + skinfile = ToLower(skinfile); + erase_extension(skinfile); + replace_slashes(datafolder); + auto vehicle_path = std::filesystem::path(ToLower(datafolder)) / ToLower(mmdfile); + + auto it = bank.vehicles.find(vehicle_path); + if (it != bank.vehicles.end()) { + dyn.vehicle = it->second; + bool found = false; + + for (const std::shared_ptr &vehicle_skin : it->second->matching_skinsets) { + if (vehicle_skin->skin != skinfile) + continue; + + dyn.skin = vehicle_skin; + found = true; + break; + } + if (!found && skinfile != "none") + ErrorLog("skin not found: " + skinfile + ", vehicle type: " + vehicle_path.string(), logtype::file); + } + else { + ErrorLog("vehicle type not found: " + vehicle_path.string(), logtype::file); + } parser.getTokens(); if (parser.peek() != "enddynamic") { diff --git a/launcher/scenery_scanner.h b/launcher/scenery_scanner.h index 7375db6d..330fd4ae 100644 --- a/launcher/scenery_scanner.h +++ b/launcher/scenery_scanner.h @@ -5,20 +5,19 @@ #include "utilities.h" #include "parser.h" #include "textures_scanner.h" -#include "textures_scanner.h" struct dynamic_desc { std::string name; std::string drivertype; - std::string skin; - std::string mmd; std::string loadtype; int loadcount; - std::string coupling; + unsigned int coupling; + std::string params; -// deferred_image mini; + std::shared_ptr vehicle; + std::shared_ptr skin; }; struct trainset_desc { @@ -55,6 +54,8 @@ struct scenery_desc { class scenery_scanner { public: + scenery_scanner(ui::vehicles_bank &bank); + std::vector scenarios; void scan(); @@ -62,4 +63,6 @@ public: private: void scan_scn(std::filesystem::path path); void parse_trainset(cParser &parser); + + ui::vehicles_bank &bank; }; diff --git a/launcher/textures_scanner.cpp b/launcher/textures_scanner.cpp index 14140cb2..4544103b 100644 --- a/launcher/textures_scanner.cpp +++ b/launcher/textures_scanner.cpp @@ -9,7 +9,7 @@ void ui::vehicles_bank::scan_textures() continue; std::fstream stream(f.path(), std::ios_base::binary | std::ios_base::in); - ctx_path = f.path().parent_path(); + ctx_path = std::filesystem::relative(f.path().parent_path(), "dynamic/"); std::string line; while (std::getline(stream, line)) @@ -47,7 +47,9 @@ void ui::vehicles_bank::parse_entry(const std::string &line) parse_texture_rule(target.substr(2), param); else if (line[0] == '*') parse_coupling_rule(target.substr(1), param); - else if (line[0] != '@') + else if (line[0] == '@') + parse_controllable_entry(target.substr(1), param); + else parse_texture_info(target, param, comments); } } @@ -92,6 +94,12 @@ void ui::vehicles_bank::parse_category_entry(const std::string ¶m) category_icons.emplace(ctx_type, "textures/mini/" + ToLower(mini)); } +void ui::vehicles_bank::parse_controllable_entry(const std::string &target, const std::string ¶m) +{ + get_vehicle(target)->controllable = + (param.size() >= 1 && param[0] == '1'); +} + void ui::vehicles_bank::parse_texture_info(const std::string &target, const std::string ¶m, const std::string &comment) { std::istringstream stream(param); @@ -111,21 +119,25 @@ void ui::vehicles_bank::parse_texture_info(const std::string &target, const std: if (!miniplus.empty()) set.mini = std::move(deferred_image("textures/mini/" + ToLower(miniplus))); + set.skin = ToLower(target); + erase_extension(set.skin); + /* std::istringstream tex_stream(target); std::string texture; while (std::getline(tex_stream, texture, '|')) { - auto path = ctx_path; - path.append(texture); - set.skins.push_back(path); + set.skins.push_back(ctx_path / ToLower(texture)); } + */ auto vehicle = get_vehicle(model); group_map[set.group].insert(vehicle); - vehicle->matching_skinsets.push_back(std::move(set)); + vehicle->matching_skinsets.push_back(std::make_shared(std::move(set))); } void ui::vehicles_bank::parse_coupling_rule(const std::string &target, const std::string ¶m) { + auto target_vehicle = get_vehicle(target); + if (param == "?") return; @@ -143,7 +155,7 @@ void ui::vehicles_bank::parse_coupling_rule(const std::string &target, const std std::getline(stream, param2, ','); coupling_rule rule; - rule.coupled_vehicle = get_vehicle(target); + rule.coupled_vehicle = target_vehicle; rule.coupling_flag = coupling_flag; get_vehicle(connected)->coupling_rules.push_back(rule); @@ -151,6 +163,8 @@ void ui::vehicles_bank::parse_coupling_rule(const std::string &target, const std void ui::vehicles_bank::parse_texture_rule(const std::string &target, const std::string ¶m) { + auto target_vehicle = get_vehicle(target); + std::istringstream stream(param); std::string prev; @@ -171,14 +185,15 @@ void ui::vehicles_bank::parse_texture_rule(const std::string &target, const std: rule.previous_vehicle = get_vehicle(prev); rule.replace_rules.emplace_back(src, dst); - get_vehicle(target)->texture_rules.push_back(rule); + target_vehicle->texture_rules.push_back(rule); } } std::shared_ptr ui::vehicles_bank::get_vehicle(const std::string &name) { - auto path = ctx_path; - path.append(name); + auto path = ctx_path / ToLower(name); + path = std::filesystem::path(path.generic_string()); + auto it = vehicles.find(path); if (it != vehicles.end()) { return it->second; diff --git a/launcher/textures_scanner.h b/launcher/textures_scanner.h index 2599d51a..4c962467 100644 --- a/launcher/textures_scanner.h +++ b/launcher/textures_scanner.h @@ -23,7 +23,8 @@ enum class vehicle_type { }; struct skin_set { - std::vector skins; + std::string skin; + //std::vector skins; deferred_image mini; std::string group; }; @@ -43,9 +44,11 @@ struct vehicle_desc { vehicle_type type; std::filesystem::path path; - std::vector matching_skinsets; + std::vector> matching_skinsets; std::vector coupling_rules; std::vector texture_rules; + + bool controllable; }; class vehicles_bank { @@ -60,6 +63,7 @@ public: private: void parse_entry(const std::string &line); void parse_category_entry(const std::string ¶m); + void parse_controllable_entry(const std::string &target, const std::string ¶m); void parse_coupling_rule(const std::string &target, const std::string ¶m); void parse_texture_rule(const std::string &target, const std::string ¶m); void parse_texture_info(const std::string &target, const std::string ¶m, const std::string &comment); diff --git a/launcher/vehicle_picker.cpp b/launcher/vehicle_picker.cpp index 9f575c44..12f43169 100644 --- a/launcher/vehicle_picker.cpp +++ b/launcher/vehicle_picker.cpp @@ -76,8 +76,8 @@ void ui::vehiclepicker_panel::render() continue; for (auto const &skinset : vehicle->matching_skinsets) { - bool map_group_eq = (skinset.group == group); - bool sel_group_eq = (selected_group && skinset.group == *selected_group); + bool map_group_eq = (skinset->group == group); + bool sel_group_eq = (selected_group && skinset->group == *selected_group); if (!model_added && map_group_eq) { model_list.push_back(&group); @@ -86,7 +86,7 @@ void ui::vehiclepicker_panel::render() if (map_sel_eq) { if (sel_group_eq) - skinset_list.push_back(&skinset); + skinset_list.push_back(skinset.get()); } else if (model_added) { can_break = true; @@ -126,7 +126,7 @@ void ui::vehiclepicker_panel::render() if (selected_vehicle == desc && selected_type == desc->type) { for (auto &skin : desc->matching_skinsets) - skinset_list.push_back(&skin); + skinset_list.push_back(skin.get()); } } @@ -138,8 +138,8 @@ void ui::vehiclepicker_panel::render() const deferred_image *image = nullptr; for (auto const &skinset : desc->matching_skinsets) { - if (skinset.mini) { - image = &skinset.mini; + if (skinset->mini) { + image = &skinset->mini; break; } } @@ -160,7 +160,8 @@ void ui::vehiclepicker_panel::render() for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { auto skin = skinset_list[i]; - std::string label = skin->skins[0].stem().string(); + //std::string label = skin->skins[0].stem().string(); + std::string label = skin->skin; if (selectable_image(label.c_str(), skin == selected_skinset, &skin->mini, true)) selected_skinset = skin; } @@ -170,49 +171,6 @@ void ui::vehiclepicker_panel::render() ImGui::PopStyleVar(); } ImGui::End(); -/* - if (!lastdef) - return; - - ImGui::Begin("target", nullptr, ImGuiWindowFlags_HorizontalScrollbar); - - int originX = ImGui::GetCursorPosX(); - - ImGui::SameLine(originX - 5); - ImGui::Dummy(ImVec2(15, 30)); - if (ImGui::BeginDragDropTarget()) { - ImGui::AcceptDragDropPayload("skin"); - ImGui::EndDragDropTarget(); - } - ImGui::SameLine(originX + 5); - - for (int i = 0; i < 20; i++) { - ImGui::PushID(i); - - GLuint tex = lastdef->get(); - if (tex != -1) { - glm::ivec2 size = lastdef->size(); - float width = 30.0f / size.y * size.x; - ImGui::Image(reinterpret_cast(tex), ImVec2(width, 30), ImVec2(0, 1), ImVec2(1, 0)); - } - - ImGui::SameLine(0, 0); - - int originX = ImGui::GetCursorPosX(); - - ImGui::SameLine(originX - 5); - ImGui::Dummy(ImVec2(15, 30)); - if (ImGui::BeginDragDropTarget()) { - ImGui::AcceptDragDropPayload("skin"); - ImGui::EndDragDropTarget(); - } - ImGui::SameLine(originX + 5); - - ImGui::PopID(); - } - - ImGui::End(); - */ } bool ui::vehiclepicker_panel::selectable_image(const char *desc, bool selected, const deferred_image* image, bool pickable) diff --git a/scene.h b/scene.h index 7237efcc..fa8fb55c 100644 --- a/scene.h +++ b/scene.h @@ -58,6 +58,7 @@ struct scratch_data { std::string name; bool initialized { false }; + //std::vector *trainsets_desc; }; // basic element of rudimentary partitioning scheme for the section. fixed size, no further subdivision diff --git a/translation.h b/translation.h index 570fe83d..57d3a8c9 100644 --- a/translation.h +++ b/translation.h @@ -30,7 +30,7 @@ private: extern locale Translations; -#define STR(x) Translations.lookup_s(x, true) +#define STR(x) Translations.lookup_s(x, false) #define STR_C(x) Translations.lookup_c(x, true) #define STRN(x) x diff --git a/utilities.cpp b/utilities.cpp index 5ef63cd3..98087d71 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -436,9 +436,9 @@ erase_leading_slashes( std::string &Filename ) { void replace_slashes( std::string &Filename ) { - std::replace( - std::begin( Filename ), std::end( Filename ), - '\\', '/' ); + std::replace( + std::begin( Filename ), std::end( Filename ), + '\\', '/' ); } // returns potential path part from provided file name