mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
Script: Add Unicode support
If the script file is UTF-8 encoded, all Lua strings are converted to/from as if they were UTF-8. Otherwise things continue to work as they have until now. Additionally, UTF-8 scripts cannot use deprecated features at all (PROPERTIES, GetStringValue).
This commit is contained in:
parent
5fcf4b785d
commit
6560518037
@ -112,18 +112,12 @@ void MeasureScript::ReadOptions(ConfigParser& parser, const WCHAR* section)
|
||||
{
|
||||
UninitializeLuaScript();
|
||||
|
||||
lua_State* L = LuaManager::GetState();
|
||||
if (m_LuaScript.Initialize(scriptFile))
|
||||
{
|
||||
bool hasInitializeFunction = m_LuaScript.IsFunction(g_InitializeFunctionName);
|
||||
m_HasUpdateFunction = m_LuaScript.IsFunction(g_UpdateFunctionName);
|
||||
m_HasGetStringFunction = m_LuaScript.IsFunction(g_GetStringFunctionName); // For backwards compatbility
|
||||
|
||||
if (m_HasGetStringFunction)
|
||||
{
|
||||
LogWarningF(this, L"Script: Using deprecated GetStringValue()");
|
||||
}
|
||||
|
||||
lua_State* L = m_LuaScript.GetState();
|
||||
lua_rawgeti(L, LUA_GLOBALSINDEX, m_LuaScript.GetRef());
|
||||
|
||||
*(MeterWindow**)lua_newuserdata(L, sizeof(MeterWindow*)) = m_MeterWindow;
|
||||
@ -136,28 +130,35 @@ void MeasureScript::ReadOptions(ConfigParser& parser, const WCHAR* section)
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setfield(L, -2, "SELF");
|
||||
|
||||
// For backwards compatibility
|
||||
lua_getfield(L, -1, "PROPERTIES");
|
||||
if (lua_isnil(L, -1) == 0)
|
||||
if (!m_LuaScript.IsUnicode())
|
||||
{
|
||||
lua_pushnil(L);
|
||||
// For backwards compatibility.
|
||||
|
||||
// Look in the table for values to read from the section
|
||||
while (lua_next(L, -2))
|
||||
m_HasGetStringFunction = m_LuaScript.IsFunction(g_GetStringFunctionName);
|
||||
if (m_HasGetStringFunction)
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
const char* strKey = lua_tostring(L, -1);
|
||||
LogWarningF(this, L"Script: Using deprecated GetStringValue()");
|
||||
}
|
||||
|
||||
std::wstring wstrKey = StringUtil::Widen(strKey);
|
||||
const std::wstring& wstrValue = parser.ReadString(section, wstrKey.c_str(), L"");
|
||||
lua_getfield(L, -1, "PROPERTIES");
|
||||
if (lua_isnil(L, -1) == 0)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
|
||||
if (!wstrValue.empty())
|
||||
// Look in the table for values to read from the section
|
||||
while (lua_next(L, -2))
|
||||
{
|
||||
std::string strStrVal = StringUtil::Narrow(wstrValue);
|
||||
const char* strValue = strStrVal.c_str();
|
||||
|
||||
lua_pushstring(L, strValue);
|
||||
lua_setfield(L, -3, strKey);
|
||||
lua_pop(L, 1);
|
||||
const char* strKey = lua_tostring(L, -1);
|
||||
const std::wstring wstrKey = StringUtil::Widen(strKey);
|
||||
const std::wstring& wstrValue =
|
||||
parser.ReadString(section, wstrKey.c_str(), L"");
|
||||
if (!wstrValue.empty())
|
||||
{
|
||||
const std::string strStrVal = StringUtil::Narrow(wstrValue);
|
||||
lua_pushstring(L, strStrVal.c_str());
|
||||
lua_setfield(L, -3, strKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,8 +194,7 @@ void MeasureScript::ReadOptions(ConfigParser& parser, const WCHAR* section)
|
||||
*/
|
||||
void MeasureScript::Command(const std::wstring& command)
|
||||
{
|
||||
std::string str = StringUtil::Narrow(command);
|
||||
m_LuaScript.RunString(str.c_str());
|
||||
m_LuaScript.RunString(command);
|
||||
}
|
||||
|
||||
//static void stackDump(lua_State *L)
|
||||
|
@ -24,6 +24,8 @@
|
||||
int LuaManager::c_RefCount = 0;
|
||||
lua_State* LuaManager::c_State = 0;
|
||||
|
||||
bool LuaManager::s_UnicodeState = false;
|
||||
|
||||
void LuaManager::Initialize()
|
||||
{
|
||||
if (c_State == nullptr)
|
||||
@ -57,8 +59,9 @@ void LuaManager::Finalize()
|
||||
}
|
||||
}
|
||||
|
||||
void LuaManager::ReportErrors(lua_State* L, const std::wstring& file)
|
||||
void LuaManager::ReportErrors(const std::wstring& file)
|
||||
{
|
||||
lua_State* L = c_State;
|
||||
const char* error = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
@ -70,25 +73,31 @@ void LuaManager::ReportErrors(lua_State* L, const std::wstring& file)
|
||||
}
|
||||
|
||||
std::wstring str(file, file.find_last_of(L'\\') + 1);
|
||||
str += StringUtil::Widen(error);
|
||||
str += s_UnicodeState ? StringUtil::WidenUTF8(error) : StringUtil::Widen(error);
|
||||
LogErrorF(L"Script: %s", str.c_str());
|
||||
}
|
||||
|
||||
void LuaManager::PushWide(lua_State* L, const WCHAR* str)
|
||||
void LuaManager::PushWide(const WCHAR* str)
|
||||
{
|
||||
const std::string narrowStr = StringUtil::Narrow(str);
|
||||
lua_State* L = c_State;
|
||||
const std::string narrowStr = s_UnicodeState ?
|
||||
StringUtil::NarrowUTF8(str) : StringUtil::Narrow(str);
|
||||
lua_pushlstring(L, narrowStr.c_str(), narrowStr.length());
|
||||
}
|
||||
|
||||
void LuaManager::PushWide(lua_State* L, const std::wstring& str)
|
||||
void LuaManager::PushWide(const std::wstring& str)
|
||||
{
|
||||
const std::string narrowStr = StringUtil::Narrow(str);
|
||||
lua_State* L = c_State;
|
||||
const std::string narrowStr = s_UnicodeState ?
|
||||
StringUtil::NarrowUTF8(str) : StringUtil::Narrow(str);
|
||||
lua_pushlstring(L, narrowStr.c_str(), narrowStr.length());
|
||||
}
|
||||
|
||||
std::wstring LuaManager::ToWide(lua_State* L, int narg)
|
||||
std::wstring LuaManager::ToWide(int narg)
|
||||
{
|
||||
lua_State* L = c_State;
|
||||
size_t strLen = 0;
|
||||
const char* str = lua_tolstring(L, narg, &strLen);
|
||||
return StringUtil::Widen(str, (int)strLen);
|
||||
return s_UnicodeState ?
|
||||
StringUtil::WidenUTF8(str, (int)strLen) : StringUtil::Widen(str, (int)strLen);
|
||||
}
|
||||
|
@ -32,13 +32,13 @@ public:
|
||||
static void Initialize();
|
||||
static void Finalize();
|
||||
|
||||
static lua_State* GetState() { return c_State; }
|
||||
static lua_State* GetState(bool unicode) { s_UnicodeState = unicode; return c_State; }
|
||||
|
||||
static void ReportErrors(lua_State* L, const std::wstring& file);
|
||||
static void ReportErrors(const std::wstring& file);
|
||||
|
||||
static void PushWide(lua_State* L, const WCHAR* str);
|
||||
static void PushWide(lua_State* L, const std::wstring& str);
|
||||
static std::wstring ToWide(lua_State* L, int narg);
|
||||
static void PushWide(const WCHAR* str);
|
||||
static void PushWide(const std::wstring& str);
|
||||
static std::wstring ToWide(int narg);
|
||||
|
||||
protected:
|
||||
static int c_RefCount;
|
||||
@ -50,6 +50,10 @@ private:
|
||||
static void RegisterMeter(lua_State* L);
|
||||
static void RegisterMeterWindow(lua_State* L);
|
||||
static void RegisterMeterString(lua_State* L);
|
||||
|
||||
// If set true |true|, Lua strings converted to/from as if they were encoded in UTF-8. Otherwise
|
||||
// Lua strings are treated as if they are encoded in the default system encoding.
|
||||
static bool s_UnicodeState;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -26,7 +26,8 @@
|
||||
**
|
||||
*/
|
||||
LuaScript::LuaScript() :
|
||||
m_Ref(LUA_NOREF)
|
||||
m_Ref(LUA_NOREF),
|
||||
m_Unicode(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -43,29 +44,36 @@ bool LuaScript::Initialize(const std::wstring& scriptFile)
|
||||
{
|
||||
assert(!IsInitialized());
|
||||
|
||||
lua_State* L = LuaManager::GetState();
|
||||
|
||||
// Load file into a buffer as luaL_loadfile does not support Unicode paths.
|
||||
FILE* file = _wfopen(scriptFile.c_str(), L"rb");
|
||||
if (!file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!file) return false;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fileSize = ftell(file);
|
||||
|
||||
char* fileData = new char[fileSize];
|
||||
const long fileSize = ftell(file);
|
||||
BYTE* fileData = new BYTE[fileSize];
|
||||
fseek(file, 0, SEEK_SET);
|
||||
fread(fileData, fileSize, 1, file);
|
||||
|
||||
fclose(file);
|
||||
file = nullptr;
|
||||
|
||||
int load = luaL_loadbuffer(L, fileData, fileSize, "");
|
||||
// Has UTF8 BOM, so assume that data is already in UTF8.
|
||||
char* scriptData = (char*)fileData;
|
||||
int scriptDataSize = fileSize;
|
||||
|
||||
// Treat the script as Unicode if it has the UTF-8 BOM.
|
||||
m_Unicode = fileSize > 3 && fileData[0] == 0xEF && fileData[1] == 0xBB && fileData[2] == 0xBF;
|
||||
if (m_Unicode)
|
||||
{
|
||||
// Skip the BOM.
|
||||
scriptData += 3;
|
||||
scriptDataSize -= 3;
|
||||
}
|
||||
|
||||
lua_State* L = GetState();
|
||||
bool scriptLoaded = luaL_loadbuffer(L, scriptData, scriptDataSize, "") == 0;
|
||||
|
||||
delete [] fileData;
|
||||
|
||||
if (load == 0)
|
||||
if (scriptLoaded)
|
||||
{
|
||||
// Create the table this script will reside in
|
||||
lua_newtable(L);
|
||||
@ -100,13 +108,13 @@ bool LuaScript::Initialize(const std::wstring& scriptFile)
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaManager::ReportErrors(L, scriptFile);
|
||||
LuaManager::ReportErrors(scriptFile);
|
||||
Uninitialize();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaManager::ReportErrors(L, scriptFile);
|
||||
LuaManager::ReportErrors(scriptFile);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -114,7 +122,7 @@ bool LuaScript::Initialize(const std::wstring& scriptFile)
|
||||
|
||||
void LuaScript::Uninitialize()
|
||||
{
|
||||
lua_State* L = LuaManager::GetState();
|
||||
lua_State* L = GetState();
|
||||
|
||||
if (m_Ref != LUA_NOREF)
|
||||
{
|
||||
@ -130,7 +138,7 @@ void LuaScript::Uninitialize()
|
||||
*/
|
||||
bool LuaScript::IsFunction(const char* funcName)
|
||||
{
|
||||
lua_State* L = LuaManager::GetState();
|
||||
lua_State* L = GetState();
|
||||
bool bExists = false;
|
||||
|
||||
if (IsInitialized())
|
||||
@ -156,7 +164,7 @@ bool LuaScript::IsFunction(const char* funcName)
|
||||
*/
|
||||
void LuaScript::RunFunction(const char* funcName)
|
||||
{
|
||||
lua_State* L = LuaManager::GetState();
|
||||
lua_State* L = GetState();
|
||||
|
||||
if (IsInitialized())
|
||||
{
|
||||
@ -168,7 +176,7 @@ void LuaScript::RunFunction(const char* funcName)
|
||||
|
||||
if (lua_pcall(L, 0, 0, 0))
|
||||
{
|
||||
LuaManager::ReportErrors(L, m_File);
|
||||
LuaManager::ReportErrors(m_File);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
@ -181,7 +189,7 @@ void LuaScript::RunFunction(const char* funcName)
|
||||
*/
|
||||
int LuaScript::RunFunctionWithReturn(const char* funcName, double& numValue, std::wstring& strValue)
|
||||
{
|
||||
lua_State* L = LuaManager::GetState();
|
||||
lua_State* L = GetState();
|
||||
int type = LUA_TNIL;
|
||||
|
||||
if (IsInitialized())
|
||||
@ -194,7 +202,7 @@ int LuaScript::RunFunctionWithReturn(const char* funcName, double& numValue, std
|
||||
|
||||
if (lua_pcall(L, 0, 1, 0))
|
||||
{
|
||||
LuaManager::ReportErrors(L, m_File);
|
||||
LuaManager::ReportErrors(m_File);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else
|
||||
@ -208,7 +216,8 @@ int LuaScript::RunFunctionWithReturn(const char* funcName, double& numValue, std
|
||||
{
|
||||
size_t strLen = 0;
|
||||
const char* str = lua_tolstring(L, -1, &strLen);
|
||||
strValue = StringUtil::Widen(str, (int)strLen);
|
||||
strValue = m_Unicode ?
|
||||
StringUtil::WidenUTF8(str, (int)strLen) : StringUtil::Widen(str, (int)strLen);
|
||||
numValue = strtod(str, nullptr);
|
||||
}
|
||||
|
||||
@ -223,16 +232,19 @@ int LuaScript::RunFunctionWithReturn(const char* funcName, double& numValue, std
|
||||
** Runs given string in the context of the script file.
|
||||
**
|
||||
*/
|
||||
void LuaScript::RunString(const char* str)
|
||||
void LuaScript::RunString(const std::wstring& str)
|
||||
{
|
||||
lua_State* L = LuaManager::GetState();
|
||||
lua_State* L = GetState();
|
||||
|
||||
if (IsInitialized())
|
||||
{
|
||||
const std::string narrowStr = m_Unicode ?
|
||||
StringUtil::NarrowUTF8(str) : StringUtil::Narrow(str);
|
||||
|
||||
// Load the string as a Lua chunk
|
||||
if (luaL_loadstring(L, str))
|
||||
if (luaL_loadstring(L, narrowStr.c_str()))
|
||||
{
|
||||
LuaManager::ReportErrors(L, m_File);
|
||||
LuaManager::ReportErrors(m_File);
|
||||
}
|
||||
|
||||
// Push our table onto the stack
|
||||
@ -243,7 +255,7 @@ void LuaScript::RunString(const char* str)
|
||||
|
||||
if (lua_pcall(L, 0, 0, 0))
|
||||
{
|
||||
LuaManager::ReportErrors(L, m_File);
|
||||
LuaManager::ReportErrors(m_File);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,18 +31,21 @@ public:
|
||||
void Uninitialize();
|
||||
bool IsInitialized() { return m_Ref != LUA_NOREF; }
|
||||
|
||||
int GetRef() { return m_Ref; }
|
||||
const std::wstring& GetFile() { return m_File; }
|
||||
int GetRef() { return m_Ref; }
|
||||
bool IsUnicode() const { return m_Unicode; }
|
||||
|
||||
lua_State* GetState() { return LuaManager::GetState(m_Unicode); }
|
||||
|
||||
bool IsFunction(const char* funcName);
|
||||
void RunFunction(const char* funcName);
|
||||
int RunFunctionWithReturn(const char* funcName, double& numValue, std::wstring& strValue);
|
||||
void RunString(const char* str);
|
||||
void RunString(const std::wstring& str);
|
||||
|
||||
protected:
|
||||
int m_Ref;
|
||||
|
||||
std::wstring m_File;
|
||||
int m_Ref;
|
||||
bool m_Unicode;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -29,7 +29,7 @@
|
||||
static int GetName(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
LuaManager::PushWide(L, self->GetName());
|
||||
LuaManager::PushWide(self->GetName());
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -40,10 +40,10 @@ static int GetOption(lua_State* L)
|
||||
MeterWindow* meterWindow = self->GetMeterWindow();
|
||||
ConfigParser& parser = meterWindow->GetParser();
|
||||
|
||||
std::wstring strTmp = LuaManager::ToWide(L, 2);
|
||||
strTmp = parser.ReadString(self->GetName(), strTmp.c_str(), LuaManager::ToWide(L, 3).c_str());
|
||||
std::wstring strTmp = LuaManager::ToWide(2);
|
||||
strTmp = parser.ReadString(self->GetName(), strTmp.c_str(), LuaManager::ToWide(3).c_str());
|
||||
|
||||
LuaManager::PushWide(L, strTmp);
|
||||
LuaManager::PushWide(strTmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ static int GetNumberOption(lua_State* L)
|
||||
MeterWindow* meterWindow = self->GetMeterWindow();
|
||||
ConfigParser& parser = meterWindow->GetParser();
|
||||
|
||||
std::wstring strTmp = LuaManager::ToWide(L, 2);
|
||||
std::wstring strTmp = LuaManager::ToWide(2);
|
||||
double value = parser.ReadFloat(self->GetName(), strTmp.c_str(), lua_tonumber(L, 3));
|
||||
|
||||
lua_pushnumber(L, value);
|
||||
@ -127,7 +127,7 @@ static int GetStringValue(lua_State* L)
|
||||
bool percentual = lua_toboolean(L, 5);
|
||||
|
||||
const WCHAR* val = self->GetStringOrFormattedValue(autoScale, scale, decimals, percentual);
|
||||
LuaManager::PushWide(L, val);
|
||||
LuaManager::PushWide(val);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
static int GetName(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
LuaManager::PushWide(L, self->GetName());
|
||||
LuaManager::PushWide(self->GetName());
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -40,10 +40,10 @@ static int GetOption(lua_State* L)
|
||||
MeterWindow* meterWindow = self->GetMeterWindow();
|
||||
ConfigParser& parser = meterWindow->GetParser();
|
||||
|
||||
std::wstring strTmp = LuaManager::ToWide(L, 2);
|
||||
std::wstring strTmp = LuaManager::ToWide(2);
|
||||
strTmp = parser.ReadString(self->GetName(), strTmp.c_str(), L"");
|
||||
|
||||
LuaManager::PushWide(L, strTmp);
|
||||
LuaManager::PushWide(strTmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ static int SetText(lua_State* L)
|
||||
if (self->GetTypeID() == TypeID<MeterString>())
|
||||
{
|
||||
MeterString* string = (MeterString*)self;
|
||||
std::wstring str = LuaManager::ToWide(L, 2);
|
||||
std::wstring str = LuaManager::ToWide(2);
|
||||
string->SetText(str.c_str());
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ static int Bang(lua_State* L)
|
||||
DECLARE_SELF(L)
|
||||
ConfigParser& parser = self->GetParser();
|
||||
|
||||
std::wstring bang = LuaManager::ToWide(L, 2);
|
||||
std::wstring bang = LuaManager::ToWide(2);
|
||||
|
||||
int top = lua_gettop(L);
|
||||
if (top == 2) // 1 argument
|
||||
@ -49,7 +49,7 @@ static int Bang(lua_State* L)
|
||||
std::vector<std::wstring> args;
|
||||
for (int i = 3; i <= top; ++i)
|
||||
{
|
||||
std::wstring tmpSz = LuaManager::ToWide(L, i);
|
||||
std::wstring tmpSz = LuaManager::ToWide(i);
|
||||
parser.ReplaceVariables(tmpSz);
|
||||
args.push_back(tmpSz);
|
||||
}
|
||||
@ -64,7 +64,7 @@ static int Bang(lua_State* L)
|
||||
static int GetMeter(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
const std::wstring meterName = LuaManager::ToWide(L, 2);
|
||||
const std::wstring meterName = LuaManager::ToWide(2);
|
||||
|
||||
Meter* meter = self->GetMeter(meterName);
|
||||
if (meter)
|
||||
@ -84,7 +84,7 @@ static int GetMeter(lua_State* L)
|
||||
static int GetMeasure(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
const std::wstring measureName = LuaManager::ToWide(L, 2);
|
||||
const std::wstring measureName = LuaManager::ToWide(2);
|
||||
|
||||
Measure* measure = self->GetMeasure(measureName);
|
||||
if (measure)
|
||||
@ -104,12 +104,12 @@ static int GetMeasure(lua_State* L)
|
||||
static int GetVariable(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
std::wstring strTmp = LuaManager::ToWide(L, 2);
|
||||
std::wstring strTmp = LuaManager::ToWide(2);
|
||||
|
||||
const std::wstring* value = self->GetParser().GetVariable(strTmp);
|
||||
if (value)
|
||||
{
|
||||
LuaManager::PushWide(L, *value);
|
||||
LuaManager::PushWide(*value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -122,11 +122,11 @@ static int GetVariable(lua_State* L)
|
||||
static int ReplaceVariables(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
std::wstring strTmp = LuaManager::ToWide(L, 2);
|
||||
std::wstring strTmp = LuaManager::ToWide(2);
|
||||
|
||||
self->GetParser().ReplaceVariables(strTmp);
|
||||
self->GetParser().ReplaceMeasures(strTmp);
|
||||
LuaManager::PushWide(L, strTmp);
|
||||
LuaManager::PushWide(strTmp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -134,7 +134,7 @@ static int ReplaceVariables(lua_State* L)
|
||||
static int ParseFormula(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
std::wstring strTmp = LuaManager::ToWide(L, 2);
|
||||
std::wstring strTmp = LuaManager::ToWide(2);
|
||||
|
||||
double result;
|
||||
if (!self->GetParser().ParseFormula(strTmp, &result))
|
||||
@ -202,9 +202,9 @@ static int GetY(lua_State* L)
|
||||
static int MakePathAbsolute(lua_State* L)
|
||||
{
|
||||
DECLARE_SELF(L)
|
||||
std::wstring path = LuaManager::ToWide(L, 2);
|
||||
std::wstring path = LuaManager::ToWide(2);
|
||||
self->MakePathAbsolute(path);
|
||||
LuaManager::PushWide(L, path);
|
||||
LuaManager::PushWide(path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user