Commit 4160df10 authored by wind2009's avatar wind2009

Merge branch 'master' of https://github.com/Fluorohydride/ygopro into develop

parents a46a7d35 bdd3995e
...@@ -13,9 +13,8 @@ jobs: ...@@ -13,9 +13,8 @@ jobs:
matrix: matrix:
name: name:
- windows - windows
- windows-xp # - windows-irrklang
- windows-irrklang # - windows-no-dxsdk
- windows-no-dxsdk
- windows-x64 - windows-x64
# - windows-2025 # - windows-2025
include: include:
...@@ -23,20 +22,15 @@ jobs: ...@@ -23,20 +22,15 @@ jobs:
os: windows-2022 os: windows-2022
vs: vs2022 vs: vs2022
audiolib: miniaudio audiolib: miniaudio
- name: windows-xp # - name: windows-irrklang
os: windows-2019 # os: windows-2022
vs: vs2019 # vs: vs2022
audiolib: miniaudio # audiolib: irrklang
xp: true # - name: windows-no-dxsdk
- name: windows-irrklang # os: windows-2022
os: windows-2022 # vs: vs2022
vs: vs2022 # audiolib: miniaudio
audiolib: irrklang # nodxsdk: true
- name: windows-no-dxsdk
os: windows-2022
vs: vs2022
audiolib: miniaudio
nodxsdk: true
- name: windows-x64 - name: windows-x64
os: windows-2022 os: windows-2022
vs: vs2022 vs: vs2022
...@@ -44,7 +38,7 @@ jobs: ...@@ -44,7 +38,7 @@ jobs:
x64: true x64: true
# - name: windows-2025 # - name: windows-2025
# os: windows-2025 # os: windows-2025
# vs: vs2022 # vs: vs2025 # to be enabled after the release of Visual Studio 2025
# audiolib: miniaudio # audiolib: miniaudio
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
...@@ -88,7 +82,7 @@ jobs: ...@@ -88,7 +82,7 @@ jobs:
- name: Extract libevent - name: Extract libevent
run: | run: |
tar xf ${{ steps.libevent.outputs.filepath }} tar xf ${{ steps.libevent.outputs.filepath }}
move libevent-2.1.12-stable event move libevent-2.0.22-stable event
- name: Download freetype - name: Download freetype
id: freetype id: freetype
...@@ -233,7 +227,7 @@ jobs: ...@@ -233,7 +227,7 @@ jobs:
- name: Use premake to generate Visual Studio solution - name: Use premake to generate Visual Studio solution
run: | run: |
.\premake5.exe ${{ matrix.vs }} --audio-lib=${{ matrix.audiolib }} ${{ matrix.xp && '--winxp-support' || '' }} .\premake5.exe ${{ matrix.vs }} --audio-lib=${{ matrix.audiolib }}
- name: Add msbuild to PATH - name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2 uses: microsoft/setup-msbuild@v2
...@@ -485,18 +479,18 @@ jobs: ...@@ -485,18 +479,18 @@ jobs:
matrix: matrix:
name: name:
- macos-13-intel - macos-13-intel
- macos-13-arm-cross-compile-static-link # - macos-13-arm-cross-compile-static-link
- macos-13-universal-static-link - macos-13-universal-static-link
- macos-15-arm - macos-15-arm
- macos-15-intel-cross-compile-static-link # - macos-15-intel-cross-compile-static-link
- macos-15-universal-static-link # - macos-15-universal-static-link
include: include:
- name: macos-13-intel - name: macos-13-intel
os: macos-13 os: macos-13
- name: macos-13-arm-cross-compile-static-link # - name: macos-13-arm-cross-compile-static-link
os: macos-13 # os: macos-13
cross-build-arm: true # cross-build-arm: true
static-link: true # static-link: true
- name: macos-13-universal-static-link - name: macos-13-universal-static-link
os: macos-13 os: macos-13
cross-build-intel: true cross-build-intel: true
...@@ -504,15 +498,15 @@ jobs: ...@@ -504,15 +498,15 @@ jobs:
static-link: true static-link: true
- name: macos-15-arm - name: macos-15-arm
os: macos-15 os: macos-15
- name: macos-15-intel-cross-compile-static-link # - name: macos-15-intel-cross-compile-static-link
os: macos-15 # os: macos-15
cross-build-intel: true # cross-build-intel: true
static-link: true # static-link: true
- name: macos-15-universal-static-link # - name: macos-15-universal-static-link
os: macos-15 # os: macos-15
cross-build-intel: true # cross-build-intel: true
cross-build-arm: true # cross-build-arm: true
static-link: true # static-link: true
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
john@suckerfreegames.com john@suckerfreegames.com
*/ */
#define _IRR_STATIC_LIB_
#include <irrlicht.h> #include <irrlicht.h>
#include "CGUITTFont.h" #include "CGUITTFont.h"
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include <windows.h> #include <windows.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#ifdef _MSC_VER #if defined(_MSC_VER) or defined(__MINGW32__)
#define mywcsncasecmp _wcsnicmp #define mywcsncasecmp _wcsnicmp
#define mystrncasecmp _strnicmp #define mystrncasecmp _strnicmp
#else #else
......
...@@ -165,8 +165,7 @@ void DataManager::ReadStringConfLine(const char* linebuf) { ...@@ -165,8 +165,7 @@ void DataManager::ReadStringConfLine(const char* linebuf) {
} }
} }
bool DataManager::Error(sqlite3* pDB, sqlite3_stmt* pStmt) { bool DataManager::Error(sqlite3* pDB, sqlite3_stmt* pStmt) {
errmsg[0] = '\0'; std::snprintf(errmsg, sizeof errmsg, "%s", sqlite3_errmsg(pDB));
std::strncat(errmsg, sqlite3_errmsg(pDB), sizeof errmsg - 1);
if(pStmt) if(pStmt)
sqlite3_finalize(pStmt); sqlite3_finalize(pStmt);
return false; return false;
......
...@@ -55,8 +55,7 @@ public: ...@@ -55,8 +55,7 @@ public:
template<typename ST> template<typename ST>
static void SendPacketToServer(unsigned char proto, const ST& st) { static void SendPacketToServer(unsigned char proto, const ST& st) {
auto p = duel_client_write; auto p = duel_client_write;
if (sizeof(ST) > MAX_DATA_SIZE) static_assert(sizeof(ST) <= MAX_DATA_SIZE, "Packet size is too large.");
return;
buffer_write<uint16_t>(p, (uint16_t)(1 + sizeof(ST))); buffer_write<uint16_t>(p, (uint16_t)(1 + sizeof(ST)));
buffer_write<uint8_t>(p, proto); buffer_write<uint8_t>(p, proto);
std::memcpy(p, &st, sizeof(ST)); std::memcpy(p, &st, sizeof(ST));
......
...@@ -1305,7 +1305,7 @@ bool ClientField::OnEvent(const irr::SEvent& event) { ...@@ -1305,7 +1305,7 @@ bool ClientField::OnEvent(const irr::SEvent& event) {
case MSG_SELECT_DISFIELD: { case MSG_SELECT_DISFIELD: {
if (!(hovered_location & LOCATION_ONFIELD)) if (!(hovered_location & LOCATION_ONFIELD))
break; break;
unsigned int flag = 1 << (hovered_sequence + (hovered_controler << 4) + ((hovered_location == LOCATION_MZONE) ? 0 : 8)); unsigned int flag = 0x1U << (hovered_sequence + (hovered_controler << 4) + ((hovered_location == LOCATION_MZONE) ? 0 : 8));
if (flag & selectable_field) { if (flag & selectable_field) {
if (flag & selected_field) { if (flag & selected_field) {
selected_field &= ~flag; selected_field &= ~flag;
......
...@@ -750,14 +750,14 @@ bool Game::Initialize() { ...@@ -750,14 +750,14 @@ bool Game::Initialize() {
cbAttribute = env->addComboBox(irr::core::rect<irr::s32>(60, 20 + 50 / 6, 195, 40 + 50 / 6), wFilter, COMBOBOX_ATTRIBUTE); cbAttribute = env->addComboBox(irr::core::rect<irr::s32>(60, 20 + 50 / 6, 195, 40 + 50 / 6), wFilter, COMBOBOX_ATTRIBUTE);
cbAttribute->setMaxSelectionRows(10); cbAttribute->setMaxSelectionRows(10);
cbAttribute->addItem(dataManager.GetSysString(1310), 0); cbAttribute->addItem(dataManager.GetSysString(1310), 0);
for(int filter = 0x1; filter != 0x80; filter <<= 1) for (int filter = 0; filter < ATTRIBUTES_COUNT; ++filter)
cbAttribute->addItem(dataManager.FormatAttribute(filter).c_str(), filter); cbAttribute->addItem(dataManager.FormatAttribute(0x1U << filter).c_str(), 0x1U << filter);
stRace = env->addStaticText(dataManager.GetSysString(1321), irr::core::rect<irr::s32>(10, 42 + 75 / 6, 70, 62 + 75 / 6), false, false, wFilter); stRace = env->addStaticText(dataManager.GetSysString(1321), irr::core::rect<irr::s32>(10, 42 + 75 / 6, 70, 62 + 75 / 6), false, false, wFilter);
cbRace = env->addComboBox(irr::core::rect<irr::s32>(60, 40 + 75 / 6, 195, 60 + 75 / 6), wFilter, COMBOBOX_RACE); cbRace = env->addComboBox(irr::core::rect<irr::s32>(60, 40 + 75 / 6, 195, 60 + 75 / 6), wFilter, COMBOBOX_RACE);
cbRace->setMaxSelectionRows(10); cbRace->setMaxSelectionRows(10);
cbRace->addItem(dataManager.GetSysString(1310), 0); cbRace->addItem(dataManager.GetSysString(1310), 0);
for(int filter = 0x1; filter < (1 << RACES_COUNT); filter <<= 1) for (int filter = 0; filter < RACES_COUNT; ++filter)
cbRace->addItem(dataManager.FormatRace(filter).c_str(), filter); cbRace->addItem(dataManager.FormatRace(0x1U << filter).c_str(), 0x1U << filter);
stAttack = env->addStaticText(dataManager.GetSysString(1322), irr::core::rect<irr::s32>(205, 22 + 50 / 6, 280, 42 + 50 / 6), false, false, wFilter); stAttack = env->addStaticText(dataManager.GetSysString(1322), irr::core::rect<irr::s32>(205, 22 + 50 / 6, 280, 42 + 50 / 6), false, false, wFilter);
ebAttack = env->addEditBox(L"", irr::core::rect<irr::s32>(260, 20 + 50 / 6, 340, 40 + 50 / 6), true, wFilter, EDITBOX_INPUTS); ebAttack = env->addEditBox(L"", irr::core::rect<irr::s32>(260, 20 + 50 / 6, 340, 40 + 50 / 6), true, wFilter, EDITBOX_INPUTS);
ebAttack->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); ebAttack->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
......
...@@ -542,9 +542,8 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { ...@@ -542,9 +542,8 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
else{ else{
curtime = temp_replay.pheader.seed; curtime = temp_replay.pheader.seed;
wchar_t version_info[256]{}; wchar_t version_info[256]{};
myswprintf(version_info, L"version 0x%X", temp_replay.pheader.version); myswprintf(version_info, L"version 0x%X\n", temp_replay.pheader.version);
repinfo.append(version_info); repinfo.append(version_info);
repinfo.append(L"\n");
} }
std::wcsftime(infobuf, sizeof infobuf / sizeof infobuf[0], L"%Y/%m/%d %H:%M:%S\n", std::localtime(&curtime)); std::wcsftime(infobuf, sizeof infobuf / sizeof infobuf[0], L"%Y/%m/%d %H:%M:%S\n", std::localtime(&curtime));
repinfo.append(infobuf); repinfo.append(infobuf);
......
...@@ -43,8 +43,7 @@ public: ...@@ -43,8 +43,7 @@ public:
template<typename ST> template<typename ST>
static void SendPacketToPlayer(DuelPlayer* dp, unsigned char proto, const ST& st) { static void SendPacketToPlayer(DuelPlayer* dp, unsigned char proto, const ST& st) {
auto p = net_server_write; auto p = net_server_write;
if (sizeof(ST) > MAX_DATA_SIZE) static_assert(sizeof(ST) <= MAX_DATA_SIZE, "Packet size is too large.");
return;
buffer_write<uint16_t>(p, (uint16_t)(1 + sizeof(ST))); buffer_write<uint16_t>(p, (uint16_t)(1 + sizeof(ST)));
buffer_write<uint8_t>(p, proto); buffer_write<uint8_t>(p, proto);
std::memcpy(p, &st, sizeof(ST)); std::memcpy(p, &st, sizeof(ST));
......
...@@ -11,6 +11,10 @@ project "YGOPro" ...@@ -11,6 +11,10 @@ project "YGOPro"
includedirs { "../ocgcore" } includedirs { "../ocgcore" }
links { "ocgcore", "clzma", "cspmemvfs", LUA_LIB_NAME, "sqlite3", "irrlicht", "freetype", "event" } links { "ocgcore", "clzma", "cspmemvfs", LUA_LIB_NAME, "sqlite3", "irrlicht", "freetype", "event" }
if not BUILD_LUA then
libdirs { LUA_LIB_DIR }
end
if BUILD_EVENT then if BUILD_EVENT then
includedirs { "../event/include" } includedirs { "../event/include" }
else else
...@@ -71,14 +75,14 @@ project "YGOPro" ...@@ -71,14 +75,14 @@ project "YGOPro"
entrypoint "mainCRTStartup" entrypoint "mainCRTStartup"
defines { "_IRR_WCHAR_FILESYSTEM" } defines { "_IRR_WCHAR_FILESYSTEM" }
files "ygopro.rc" files "ygopro.rc"
links { "opengl32", "ws2_32", "winmm", "gdi32", "kernel32", "user32", "imm32", "iphlpapi" } links { "ws2_32", "iphlpapi" }
if USE_AUDIO and AUDIO_LIB == "irrklang" then if USE_AUDIO and AUDIO_LIB == "irrklang" then
links { "irrKlang" } links { "irrKlang" }
if IRRKLANG_PRO then if IRRKLANG_PRO then
defines { "IRRKLANG_STATIC" } defines { "IRRKLANG_STATIC" }
filter { "not configurations:Debug" } filter { "system:windows", "not configurations:Debug" }
libdirs { IRRKLANG_PRO_RELEASE_LIB_DIR } libdirs { IRRKLANG_PRO_RELEASE_LIB_DIR }
filter { "configurations:Debug" } filter { "system:windows", "configurations:Debug" }
libdirs { IRRKLANG_PRO_DEBUG_LIB_DIR } libdirs { IRRKLANG_PRO_DEBUG_LIB_DIR }
filter {} filter {}
end end
...@@ -87,7 +91,7 @@ project "YGOPro" ...@@ -87,7 +91,7 @@ project "YGOPro"
links { "dl", "pthread" } links { "dl", "pthread" }
filter "system:macosx" filter "system:macosx"
openmp "Off" openmp "Off"
links { "z" } links { "OpenGL.framework", "Cocoa.framework", "IOKit.framework" }
defines { "GL_SILENCE_DEPRECATION" } defines { "GL_SILENCE_DEPRECATION" }
if MAC_ARM then if MAC_ARM then
linkoptions { "-arch arm64" } linkoptions { "-arch arm64" }
...@@ -98,8 +102,9 @@ project "YGOPro" ...@@ -98,8 +102,9 @@ project "YGOPro"
if USE_AUDIO and AUDIO_LIB == "irrklang" then if USE_AUDIO and AUDIO_LIB == "irrklang" then
links { "irrklang" } links { "irrklang" }
end end
filter "system:linux" filter "system:linux"
links { "GL", "X11", "Xxf86vm" } links { "GL", "X11", "Xxf86vm", "dl", "pthread" }
linkoptions { "-fopenmp" } linkoptions { "-fopenmp" }
if USE_AUDIO and AUDIO_LIB == "irrklang" then if USE_AUDIO and AUDIO_LIB == "irrklang" then
links { "IrrKlang" } links { "IrrKlang" }
......
...@@ -81,7 +81,7 @@ void Replay::EndRecord() { ...@@ -81,7 +81,7 @@ void Replay::EndRecord() {
pheader.flag |= REPLAY_COMPRESSED; pheader.flag |= REPLAY_COMPRESSED;
size_t propsize = 5; size_t propsize = 5;
comp_size = MAX_COMP_SIZE; comp_size = MAX_COMP_SIZE;
int ret = LzmaCompress(comp_data, &comp_size, replay_data, replay_size, pheader.props, &propsize, 5, 1 << 24, 3, 0, 2, 32, 1); int ret = LzmaCompress(comp_data, &comp_size, replay_data, replay_size, pheader.props, &propsize, 5, 0x1U << 24, 3, 0, 2, 32, 1);
if (ret != SZ_OK) { if (ret != SZ_OK) {
std::memcpy(comp_data, &ret, sizeof ret); std::memcpy(comp_data, &ret, sizeof ret);
comp_size = sizeof ret; comp_size = sizeof ret;
......
...@@ -747,7 +747,7 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) { ...@@ -747,7 +747,7 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) {
break; break;
} }
case MSG_AI_NAME: { case MSG_AI_NAME: {
char namebuf[128]{}; char namebuf[SIZE_AI_NAME]{};
wchar_t wname[20]{}; wchar_t wname[20]{};
int name_len = buffer_read<uint16_t>(pbuf); int name_len = buffer_read<uint16_t>(pbuf);
if (name_len + 1 <= (int)sizeof namebuf) { if (name_len + 1 <= (int)sizeof namebuf) {
...@@ -760,8 +760,8 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) { ...@@ -760,8 +760,8 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) {
break; break;
} }
case MSG_SHOW_HINT: { case MSG_SHOW_HINT: {
char msgbuf[1024]{}; char msgbuf[SIZE_HINT_MSG]{};
wchar_t msg[1024]{}; wchar_t msg[SIZE_HINT_MSG]{};
int msg_len = buffer_read<uint16_t>(pbuf); int msg_len = buffer_read<uint16_t>(pbuf);
if (msg_len + 1 <= (int)sizeof msgbuf) { if (msg_len + 1 <= (int)sizeof msgbuf) {
std::memcpy(msgbuf, pbuf, msg_len); std::memcpy(msgbuf, pbuf, msg_len);
......
...@@ -68,6 +68,7 @@ void SoundManager::RefreshBGMList() { ...@@ -68,6 +68,7 @@ void SoundManager::RefreshBGMList() {
#endif #endif
} }
void SoundManager::RefershBGMDir(std::wstring path, int scene) { void SoundManager::RefershBGMDir(std::wstring path, int scene) {
#ifdef YGOPRO_USE_AUDIO
std::wstring search = L"./sound/BGM/" + path; std::wstring search = L"./sound/BGM/" + path;
FileSystem::TraversalDir(search.c_str(), [this, &path, scene](const wchar_t* name, bool isdir) { FileSystem::TraversalDir(search.c_str(), [this, &path, scene](const wchar_t* name, bool isdir) {
if(!isdir && ( if(!isdir && (
...@@ -81,6 +82,7 @@ void SoundManager::RefershBGMDir(std::wstring path, int scene) { ...@@ -81,6 +82,7 @@ void SoundManager::RefershBGMDir(std::wstring path, int scene) {
BGMList[scene].push_back(filename); BGMList[scene].push_back(filename);
} }
}); });
#endif // YGOPRO_USE_AUDIO
} }
void SoundManager::PlaySoundEffect(int sound) { void SoundManager::PlaySoundEffect(int sound) {
#ifdef YGOPRO_USE_AUDIO #ifdef YGOPRO_USE_AUDIO
......
Subproject commit 5b74daf0697f306081fcfd6710642e692c6fdd76 Subproject commit 3e613479686a7a2bdcaf7a83e0e9e99f62d89d2d
...@@ -16,7 +16,7 @@ project "event" ...@@ -16,7 +16,7 @@ project "event"
prebuildcommands { "xcopy /E /Y $(ProjectDir)..\\event\\WIN32-Code $(ProjectDir)..\\event\\include", prebuildcommands { "xcopy /E /Y $(ProjectDir)..\\event\\WIN32-Code $(ProjectDir)..\\event\\include",
"xcopy /E /Y $(ProjectDir)..\\event\\WIN32-Code\\nmake $(ProjectDir)..\\event\\include" } "xcopy /E /Y $(ProjectDir)..\\event\\WIN32-Code\\nmake $(ProjectDir)..\\event\\include" }
files { "win32select.c", "evthread_win32.c", "buffer_iocp.c", "event_iocp.c", "bufferevent_async.c" } files { "win32select.c", "evthread_win32.c", "buffer_iocp.c", "event_iocp.c", "bufferevent_async.c" }
defines { "UINT32_MAX=0xffffffffui32" } -- quirk of libevent 2.1.2 defines { "WIN32" } -- quirk of old libevent
filter "system:linux" filter "system:linux"
files { "evthread_pthread.c", "epoll.c", "epoll_sub.c", "poll.c", "select.c" } files { "evthread_pthread.c", "epoll.c", "epoll_sub.c", "poll.c", "select.c" }
......
...@@ -138,6 +138,3 @@ project "miniaudio" ...@@ -138,6 +138,3 @@ project "miniaudio"
includedirs { OPUS_INCLUDE_DIR, OPUSFILE_INCLUDE_DIR, VORBIS_INCLUDE_DIR, OGG_INCLUDE_DIR } includedirs { OPUS_INCLUDE_DIR, OPUSFILE_INCLUDE_DIR, VORBIS_INCLUDE_DIR, OGG_INCLUDE_DIR }
end end
end end
filter "system:linux"
links { "dl", "pthread", "m" }
...@@ -10,8 +10,11 @@ BUILD_LUA = true ...@@ -10,8 +10,11 @@ BUILD_LUA = true
LUA_LIB_NAME = "lua" -- change this if you don't build Lua LUA_LIB_NAME = "lua" -- change this if you don't build Lua
BUILD_EVENT = os.istarget("windows") BUILD_EVENT = os.istarget("windows")
BUILD_FREETYPE = os.istarget("windows") BUILD_FREETYPE = os.istarget("windows")
BUILD_SQLITE = os.istarget("windows") BUILD_SQLITE = os.istarget("windows")
BUILD_IRRLICHT = true -- modified Irrlicht is required, can't use the official one BUILD_IRRLICHT = true -- modified Irrlicht is required, can't use the official one
USE_DXSDK = true USE_DXSDK = true
...@@ -290,7 +293,6 @@ workspace "YGOPro" ...@@ -290,7 +293,6 @@ workspace "YGOPro"
if MAC_ARM and MAC_INTEL then if MAC_ARM and MAC_INTEL then
architecture "universal" architecture "universal"
end end
links { "OpenGL.framework", "Cocoa.framework", "IOKit.framework" }
filter "system:linux" filter "system:linux"
buildoptions { "-U_FORTIFY_SOURCE" } buildoptions { "-U_FORTIFY_SOURCE" }
...@@ -326,7 +328,6 @@ workspace "YGOPro" ...@@ -326,7 +328,6 @@ workspace "YGOPro"
disablewarnings { "4244", "4267", "4838", "4996", "6011", "6031", "6054", "6262" } disablewarnings { "4244", "4267", "4838", "4996", "6011", "6031", "6054", "6262" }
filter { "configurations:Release", "not action:vs*" } filter { "configurations:Release", "not action:vs*" }
symbols "On"
defines "NDEBUG" defines "NDEBUG"
filter { "configurations:Debug", "action:vs*" } filter { "configurations:Debug", "action:vs*" }
......
Subproject commit a3b89a2048802d758c6a8d53cbb0d2f0f9a25613 Subproject commit 286e4ce94d20ac738748ac823de65aaffb9b2c39
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment