// Copyright 2021 Jean Pierre Cimalando // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 // #include "ysfx_parse_menu.hpp" #include "ysfx_utils.hpp" #include #include #include static void ysfx_menu_insn_clear(ysfx_menu_insn_t *insn) { delete[] insn->name; } static bool ysfx_do_create_menu(std::vector &insns, const char **str, uint32_t *startid, uint32_t menudepth) { if (menudepth >= 8) return false; /// auto shrink_insns_to = [&insns](size_t count) { assert(insns.size() >= count); while (insns.size() > count) { ysfx_menu_insn_clear(&insns.back()); insns.pop_back(); } }; /// size_t insn_count_at_start = insns.size(); /// size_t pos = 0; uint32_t id = *startid; const char *p = *str; const char *sep = strchr(p, '|'); while (sep || *p) { size_t len = sep ? (size_t)(sep - p) : strlen(p); std::string buf(p, len); p += len; if (sep) sep = strchr(++p, '|'); const char *q = buf.c_str(); bool subm = false; size_t insn_count_at_subm = 0; bool done = false; uint32_t item_flags = 0; while (*q && strchr(">#!<", *q)) { if (*q == '>' && !subm) { insn_count_at_subm = insns.size(); insns.emplace_back(); insns.back().opcode = ysfx_menu_sub; subm = ysfx_do_create_menu(insns, &p, &id, menudepth + 1); insns.emplace_back(); insns.back().opcode = ysfx_menu_endsub; sep = strchr(p, '|'); } if (*q == '#') item_flags |= ysfx_menu_item_disabled; if (*q == '!') item_flags |= ysfx_menu_item_checked; if (*q == '<') done = true; ++q; } if (*q) { if (subm) { for (ysfx_menu_insn_t *insn : {&insns[insn_count_at_subm], &insns.back()}) { insn->name = ysfx::strdup_using_new(q); insn->item_flags = item_flags; } } else { ysfx_menu_insn_t &insn = (insns.emplace_back(), insns.back()); insn.opcode = ysfx_menu_item; insn.id = id++; insn.name = ysfx::strdup_using_new(q); insn.item_flags = item_flags; } } else { if (subm) shrink_insns_to(insn_count_at_subm); if (!done) { ysfx_menu_insn_t &insn = (insns.emplace_back(), insns.back()); insn.opcode = ysfx_menu_separator; } } ++pos; if (done) break; } *str = p; *startid = id; /// if (!pos) { shrink_insns_to(insn_count_at_start); return false; } return true; } ysfx_menu_t *ysfx_parse_menu(const char *text) { std::vector insns; insns.reserve(256); /// auto cleanup = ysfx::defer([&insns]() { for (ysfx_menu_insn_t &insn : insns) ysfx_menu_insn_clear(&insn); }); /// const char *textpos = text; uint32_t id = 1; ysfx_do_create_menu(insns, &textpos, &id, 0); /// ysfx_menu_u menu{new ysfx_menu_t}; menu->insn_count = (uint32_t)insns.size(); menu->insns = new ysfx_menu_insn_t[menu->insn_count]; memcpy(menu->insns, insns.data(), menu->insn_count * sizeof(ysfx_menu_insn_t)); insns.clear(); return menu.release(); } void ysfx_menu_free(ysfx_menu_t *menu) { if (!menu) return; for (uint32_t i = 0; i < menu->insn_count; ++i) ysfx_menu_insn_clear(&menu->insns[i]); delete[] menu->insns; delete menu; }