/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #pragma once #include "Base/Logger.hpp" #include "Data/Containers/String.hpp" #include #include #include namespace OpenVulkano { namespace { std::tuple SplitAppleModelComponents(std::string_view input) { size_t commaPos = input.rfind(','); // Find the boundary between text and number size_t numberStart = commaPos - 1; while (numberStart > 0 && std::isdigit(input[numberStart - 1])) { --numberStart; } if (numberStart < 1 || commaPos == std::string_view::npos) return { input, -1, -1 }; std::string_view beforeNumber = input.substr(0, numberStart); std::string_view number = input.substr(numberStart, commaPos - numberStart); std::string_view afterComma = input.substr(commaPos + 1); int major = -1, minor = -1; c4::from_chars(c4::csubstr(number.begin(), number.length()), &major); c4::from_chars(c4::csubstr(afterComma.begin(), afterComma.length()), &minor); return { beforeNumber, major, minor }; } std::string DecodeIPhoneName(int major, int minor) { uint32_t number = std::max(major - 1, 1); std::string suffix; switch(major) { case 1: if (minor == 1) number = 1; else if (minor == 2) { number = 3; suffix = "G"; } break; case 2: number = 3; suffix = "GS"; case 4: suffix = "s"; //fallthrough case 3: number = 4; break; case 6: suffix = "s"; //fallthrough case 5: number = 5; if(minor > 2) suffix = "c"; break; case 7: if (minor == 1) suffix = " Plus"; break; case 8: if (minor == 4) return "iPhone SE"; number = 6; suffix = "s"; if (minor == 2) suffix += " Plus"; break; case 9: number = 7; if ((minor & 1) == 0) suffix = " Plus"; break; case 10: if (minor < 3) { number = 8; if (minor == 2) suffix = " Plus"; } else return "iPhone X"; break; case 11: { switch (minor) { case 2: return "iPhone XS"; case 8: return "iPhone XR"; case 4: case 6: return "iPhone XS Max"; } break; } case 12: if (minor == 8) return "iPhone SE (2nd generation)"; if (minor > 2) suffix = " Pro"; if (minor > 4) suffix += " Max"; break; case 13: if (minor == 1) { suffix = " Mini"; break; } if (minor > 2) suffix = " Pro"; if (minor == 4) suffix += " Max"; break; case 14: if (minor == 6) return "iPhone SE (3nd generation)"; if (minor == 2) suffix = " Pro"; if (minor == 3) suffix = " Pro Max"; if (minor == 4) suffix = " Mini"; if (minor > 6) number = 14; if (minor == 8) suffix = " Plus"; break; case 15: if (minor == 2) suffix = " Pro"; else if (minor == 3) suffix = " Pro Max"; else if (minor > 3) number = 15; if (minor == 5) suffix = " Plus"; break; case 16: suffix = " Pro"; if (minor == 2) suffix += " Max"; break; case 17: if (minor == 1) suffix = " Pro"; else if (minor == 2) suffix = " Pro Max"; else if (minor == 4) suffix = " Plus"; break; } return fmt::format("iPhone {}{}", number, suffix); } std::string DecodeIPadName(int major, int minor) { std::string type = "unknown"; uint32_t gen = major * 1000 + minor; std::map, std::pair> options = { {{1, 1}, {"", 1}}, {{1, 2}, {"", 1}}, {{1, 3}, {"", 1}}, {{2, 1}, {"", 2}}, {{2, 2}, {"", 2}}, {{2, 3}, {"", 2}}, {{2, 4}, {"", 2}}, {{3, 1}, {"", 3}}, {{3, 2}, {"", 3}}, {{3, 3}, {"", 3}}, {{3, 4}, {"", 4}}, {{3, 5}, {"", 4}}, {{3, 6}, {"", 4}}, {{6, 11}, {"", 5}}, {{6, 12}, {"", 5}}, {{7, 5}, {"", 6}}, {{7, 6}, {"", 6}}, {{7, 11}, {"", 7}}, {{7, 12}, {"", 7}}, {{11, 6}, {"", 8}}, {{11, 7}, {"", 8}}, {{12, 1}, {"", 9}}, {{12, 2}, {"", 9}}, {{13, 18}, {"", 10}}, {{13, 19}, {"", 10}}, {{2, 5}, {"Mini", 1}}, {{2, 6}, {"Mini", 1}}, {{2, 7}, {"Mini", 1}}, {{4, 4}, {"Mini", 2}}, {{4, 5}, {"Mini", 2}}, {{4, 6}, {"Mini", 2}}, {{4, 7}, {"Mini", 3}}, {{4, 8}, {"Mini", 3}}, {{4, 9}, {"Mini", 3}}, {{5, 1}, {"Mini", 4}}, {{5, 2}, {"Mini", 4}}, {{11, 1}, {"Mini", 5}}, {{11, 2}, {"Mini", 5}}, {{14, 1}, {"Mini", 6}}, {{14, 2}, {"Mini", 6}}, {{4, 1}, {"Air", 1}}, {{4, 2}, {"Air", 1}}, {{4, 3}, {"Air", 1}}, {{5, 3}, {"Air", 2}}, {{5, 4}, {"Air", 2}}, {{11, 3}, {"Air", 3}}, {{11, 4}, {"Air", 3}}, {{13, 1}, {"Air", 4}}, {{13, 2}, {"Air", 4}}, {{13, 16}, {"Air", 5}}, {{13, 17}, {"Air", 5}}, {{6, 7}, {"Pro 12.9\"", 1}}, {{6, 8}, {"Pro 12.9\"", 1}}, {{7, 1}, {"Pro 12.9\"", 2}}, {{7, 2}, {"Pro 12.9\"", 2}}, {{8, 5}, {"Pro 12.9\"", 3}}, {{8, 6}, {"Pro 12.9\"", 3}}, {{8, 7}, {"Pro 12.9\"", 3}}, {{8, 8}, {"Pro 12.9\"", 3}}, {{8, 11}, {"Pro 12.9\"", 4}}, {{8, 12}, {"Pro 12.9\"", 4}}, {{13, 8}, {"Pro 12.9\"", 5}}, {{13, 9}, {"Pro 12.9\"", 5}}, {{13, 10}, {"Pro 12.9\"", 5}}, {{13, 11}, {"Pro 12.9\"", 5}}, {{14, 5}, {"Pro 12.9\"", 6}}, {{14, 6}, {"Pro 12.9\"", 6}}, {{16, 5}, {"Pro 12.9\"", 7}}, {{16, 6}, {"Pro 12.9\"", 7}}, {{6, 3}, {"Pro 9.7\"", 1}}, {{6, 4}, {"Pro 9.7\"", 1}}, {{7, 3}, {"Pro 10.5\"", 1}}, {{7, 4}, {"Pro 10.5\"", 1}}, {{8, 1}, {"Pro 11\"", 1}}, {{8, 2}, {"Pro 11\"", 1}}, {{8, 3}, {"Pro 11\"", 1}}, {{8, 4}, {"Pro 11\"", 1}}, {{8, 9}, {"Pro 11\"", 2}}, {{8, 10}, {"Pro 11\"", 2}}, {{13, 4}, {"Pro 11\"", 3}}, {{13, 5}, {"Pro 11\"", 3}}, {{13, 6}, {"Pro 11\"", 3}}, {{13, 7}, {"Pro 11\"", 3}}, {{14, 3}, {"Pro 11\"", 4}}, {{14, 4}, {"Pro 11\"", 4}}, {{16, 3}, {"Pro 11\"", 5}}, {{16, 4}, {"Pro 11\"", 5}}, }; auto iter = options.find({major, minor}); if (iter != options.end()) { type = iter->second.first; gen = iter->second.second; } else { spdlog::warn("Could not decode iPad identifier iPad{},{}", major, minor); } return fmt::format("iPad {} ({}{} generation)", type, gen, String::GetOrdinal(gen)); } std::string DecodeModelString(const std::string& model) { if (model == "i386" || model == "x86_64") return "Simulator"; auto [type, major, minor] = SplitAppleModelComponents(model); if (type == "iPhone") return DecodeIPhoneName(major, minor); else if (type == "iPad") return DecodeIPadName(major, minor); else if (type == "iPod") { if (major == 7) major = 6; else if (major == 9) major = 7; return fmt::format("iPod touch ({}{} generation)", major, String::GetOrdinal(major)); } else { spdlog::warn("Could not decode identifier {}", model); } return ""; } } }