1 /*
2  *  Copyright (C) 2016-2020 Team Kodi (https://kodi.tv)
3  *
4  *  SPDX-License-Identifier: GPL-2.0-or-later
5  *  See LICENSE.md for more information.
6  */
7 
8 #include "LibretroSetting.h"
9 #include "libretro/libretro.h"
10 
11 using namespace LIBRETRO;
12 
CLibretroSetting(const retro_variable * libretroVariable)13 CLibretroSetting::CLibretroSetting(const retro_variable* libretroVariable) :
14   m_key(libretroVariable->key)
15 {
16   Parse(libretroVariable->value);
17 
18   SetCurrentValue(DefaultValue());
19 }
20 
DefaultValue() const21 const std::string& CLibretroSetting::DefaultValue() const
22 {
23   static std::string empty;
24 
25   if (!m_values.empty())
26     return m_values[0];
27 
28   return empty;
29 }
30 
Parse(const std::string & libretroValue)31 void CLibretroSetting::Parse(const std::string& libretroValue)
32 {
33   // Example retro_variable:
34   //      { "foo_option", "Speed hack coprocessor X; false|true" }
35   //
36   // Text before first ';' is description. This ';' must be followed by
37   // a space, and followed by a list of possible values split up with '|'.
38 
39   // First, Split the string into description and value list
40   auto SplitDescription = [](const std::string& retroVal, std::string& description, std::string& values)
41     {
42       // Look for ; separating the description from the pipe-separated values
43       size_t pos;
44       if ((pos = retroVal.find(';')) != std::string::npos)
45       {
46         // Set description
47         description = retroVal.substr(0, pos);
48 
49         // Advance past semicolon
50         pos++;
51 
52         // Advance past spaces
53         while (pos < retroVal.size() && retroVal[pos] == ' ')
54           pos++;
55 
56         // Set values
57         values = retroVal.substr(pos);
58       }
59       else
60       {
61         // No semicolon found
62         values = retroVal;
63       }
64     };
65 
66   std::string strDescription;
67   std::string strValues;
68   SplitDescription(libretroValue, strDescription, strValues);
69 
70   if (strDescription.empty())
71   {
72     // Use key name as a backup
73     strDescription = m_key;
74   }
75 
76   // Split the values on | delimiter
77   auto SplitValues = [](const std::string& strValues, std::vector<std::string>& vecValues)
78     {
79       std::string remainingValues = strValues;
80       while (!remainingValues.empty())
81       {
82         std::string strValue;
83 
84         size_t pos;
85         if ((pos = remainingValues.find('|')) == std::string::npos)
86         {
87           strValue = remainingValues;
88           remainingValues.clear();
89         }
90         else
91         {
92           strValue = remainingValues.substr(0, pos);
93           remainingValues.erase(0, pos + 1);
94         }
95 
96         vecValues.push_back(strValue);
97       }
98     };
99 
100   std::vector<std::string> vecValues;
101   SplitValues(strValues, vecValues);
102 
103   m_description = std::move(strDescription);
104   m_values = std::move(vecValues);
105   m_valuesStr = std::move(strValues);
106 }
107