mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-11 12:27:35 +00:00
The repo was internally inconsistent: the LICENSE file is GPL v3 but every src/ header carried the LGPL v3 notice and the README said "LGPL v3 or later". LGPL was also the wrong fit — this is an injected mod plus a bundled addon, not a library other code links against, so LGPL's linking exception buys nothing. It's also required: src/voicechat/Tts.cpp is ported from awesome_wotlk's VoiceChat.cpp, which is GPL v3, and a derivative of GPL code cannot be relicensed under more permissive terms. Dependencies stay compatible — MinHook (BSD-2) and the nampower-mirrored packet shapes (BSD) are GPL-compatible, and SAPI is covered by the system-library exception. Swept "GNU Lesser General Public License" -> "GNU General Public License" across all 257 src/ headers and changed the README license line to match the GPL v3 LICENSE file. Comment-only; no build impact.
224 lines
7.2 KiB
C++
224 lines
7.2 KiB
C++
// This file is part of ClassicAPI.
|
|
//
|
|
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
|
// of the GNU General Public License as published by the Free Software Foundation, either
|
|
// version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
// PURPOSE. See the GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with
|
|
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
#include "Game.h"
|
|
|
|
#define PICOJSON_USE_INT64
|
|
#include <picojson/picojson.h>
|
|
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <string>
|
|
|
|
namespace Encoding::JSON {
|
|
|
|
namespace {
|
|
|
|
constexpr int kMaxDepth = 64;
|
|
|
|
bool LuaToPicojson(void *L, int idx, picojson::value &out, int depth);
|
|
|
|
// Consecutive 1..N integer keys → JSON array; anything else → object.
|
|
bool IsLuaArray(void *L, int idx, int &outLen) {
|
|
outLen = 0;
|
|
int seen = 0, maxIdx = 0;
|
|
Game::Lua::PushNil(L);
|
|
while (Game::Lua::Next(L, idx) != 0) {
|
|
++seen;
|
|
if (Game::Lua::Type(L, -2) != Game::Lua::TYPE_NUMBER) {
|
|
Game::Lua::SetTop(L, -3);
|
|
return false;
|
|
}
|
|
const double k = Game::Lua::ToNumber(L, -2);
|
|
const int ki = static_cast<int>(k);
|
|
if (ki < 1 || static_cast<double>(ki) != k) {
|
|
Game::Lua::SetTop(L, -3);
|
|
return false;
|
|
}
|
|
if (ki > maxIdx) maxIdx = ki;
|
|
Game::Lua::SetTop(L, -2);
|
|
}
|
|
if (seen != maxIdx) return false;
|
|
outLen = seen;
|
|
return true;
|
|
}
|
|
|
|
bool LuaTableToPicojson(void *L, int idx, picojson::value &out, int depth) {
|
|
if (depth >= kMaxDepth) return false;
|
|
if (idx < 0) idx = Game::Lua::GetTop(L) + idx + 1;
|
|
|
|
int arrayLen = 0;
|
|
if (IsLuaArray(L, idx, arrayLen)) {
|
|
picojson::array arr;
|
|
arr.reserve(arrayLen);
|
|
for (int i = 1; i <= arrayLen; ++i) {
|
|
Game::Lua::PushNumber(L, static_cast<double>(i));
|
|
Game::Lua::RawGet(L, idx);
|
|
picojson::value child;
|
|
if (!LuaToPicojson(L, -1, child, depth + 1)) {
|
|
Game::Lua::SetTop(L, -2);
|
|
return false;
|
|
}
|
|
arr.push_back(std::move(child));
|
|
Game::Lua::SetTop(L, -2);
|
|
}
|
|
out = picojson::value(arr);
|
|
return true;
|
|
}
|
|
|
|
picojson::object obj;
|
|
Game::Lua::PushNil(L);
|
|
while (Game::Lua::Next(L, idx) != 0) {
|
|
const int keyType = Game::Lua::Type(L, -2);
|
|
std::string key;
|
|
if (keyType == Game::Lua::TYPE_STRING) {
|
|
const char *kstr = Game::Lua::ToString(L, -2);
|
|
const unsigned int klen = Game::Lua::StrLen(L, -2);
|
|
key.assign(kstr, klen);
|
|
} else if (keyType == Game::Lua::TYPE_NUMBER) {
|
|
char buf[32];
|
|
std::snprintf(buf, sizeof(buf), "%.14g",
|
|
Game::Lua::ToNumber(L, -2));
|
|
key = buf;
|
|
} else {
|
|
// Drop unsupported key types.
|
|
Game::Lua::SetTop(L, -2);
|
|
continue;
|
|
}
|
|
|
|
picojson::value child;
|
|
if (!LuaToPicojson(L, -1, child, depth + 1)) {
|
|
Game::Lua::SetTop(L, -3);
|
|
return false;
|
|
}
|
|
obj[key] = std::move(child);
|
|
Game::Lua::SetTop(L, -2);
|
|
}
|
|
out = picojson::value(obj);
|
|
return true;
|
|
}
|
|
|
|
bool LuaToPicojson(void *L, int idx, picojson::value &out, int depth) {
|
|
switch (Game::Lua::Type(L, idx)) {
|
|
case Game::Lua::TYPE_NIL:
|
|
out = picojson::value();
|
|
return true;
|
|
case Game::Lua::TYPE_BOOLEAN:
|
|
out = picojson::value(Game::Lua::ToBoolean(L, idx) != 0);
|
|
return true;
|
|
case Game::Lua::TYPE_NUMBER: {
|
|
const double n = Game::Lua::ToNumber(L, idx);
|
|
if (std::isnan(n) || std::isinf(n)) {
|
|
out = picojson::value(); // JSON has no nan/inf → null
|
|
return true;
|
|
}
|
|
out = picojson::value(n);
|
|
return true;
|
|
}
|
|
case Game::Lua::TYPE_STRING: {
|
|
const char *s = Game::Lua::ToString(L, idx);
|
|
const unsigned int n = Game::Lua::StrLen(L, idx);
|
|
out = picojson::value(std::string(s, n));
|
|
return true;
|
|
}
|
|
case Game::Lua::TYPE_TABLE:
|
|
return LuaTableToPicojson(L, idx, out, depth);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void PicojsonToLua(void *L, const picojson::value &v) {
|
|
if (v.is<picojson::null>()) {
|
|
Game::Lua::PushNil(L);
|
|
} else if (v.is<bool>()) {
|
|
Game::Lua::PushBool(L, v.get<bool>());
|
|
} else if (v.is<double>()) {
|
|
Game::Lua::PushNumber(L, v.get<double>());
|
|
} else if (v.is<int64_t>()) {
|
|
Game::Lua::PushNumber(L,
|
|
static_cast<double>(v.get<int64_t>()));
|
|
} else if (v.is<std::string>()) {
|
|
const auto &s = v.get<std::string>();
|
|
Game::Lua::PushLString(L, s.data(),
|
|
static_cast<unsigned int>(s.size()));
|
|
} else if (v.is<picojson::array>()) {
|
|
const auto &arr = v.get<picojson::array>();
|
|
Game::Lua::NewTable(L);
|
|
int i = 1;
|
|
for (const auto &child : arr) {
|
|
Game::Lua::PushNumber(L, static_cast<double>(i++));
|
|
PicojsonToLua(L, child);
|
|
Game::Lua::RawSet(L, -3);
|
|
}
|
|
} else if (v.is<picojson::object>()) {
|
|
const auto &obj = v.get<picojson::object>();
|
|
Game::Lua::NewTable(L);
|
|
for (const auto &kv : obj) {
|
|
Game::Lua::PushLString(L, kv.first.data(),
|
|
static_cast<unsigned int>(kv.first.size()));
|
|
PicojsonToLua(L, kv.second);
|
|
Game::Lua::RawSet(L, -3);
|
|
}
|
|
} else {
|
|
Game::Lua::PushNil(L);
|
|
}
|
|
}
|
|
|
|
int __fastcall Script_SerializeJSON(void *L) {
|
|
if (Game::Lua::GetTop(L) < 1) {
|
|
Game::Lua::Error(L, "Usage: C_EncodingUtil.SerializeJSON(value)");
|
|
return 0;
|
|
}
|
|
picojson::value v;
|
|
if (!LuaToPicojson(L, 1, v, 0))
|
|
return 0;
|
|
const std::string out = v.serialize();
|
|
Game::Lua::PushLString(L, out.data(),
|
|
static_cast<unsigned int>(out.size()));
|
|
return 1;
|
|
}
|
|
|
|
int __fastcall Script_DeserializeJSON(void *L) {
|
|
if (!Game::Lua::IsString(L, 1)) {
|
|
Game::Lua::Error(L, "Usage: C_EncodingUtil.DeserializeJSON(json)");
|
|
return 0;
|
|
}
|
|
const char *src = Game::Lua::ToString(L, 1);
|
|
const unsigned int srcLen = Game::Lua::StrLen(L, 1);
|
|
if (src == nullptr)
|
|
return 0;
|
|
|
|
picojson::value v;
|
|
std::string err;
|
|
const char *endp = picojson::parse(v, src, src + srcLen, &err);
|
|
if (!err.empty() || endp != src + srcLen)
|
|
return 0;
|
|
|
|
PicojsonToLua(L, v);
|
|
return 1;
|
|
}
|
|
|
|
void Register() {
|
|
Game::Lua::RegisterTableFunction("C_EncodingUtil", "SerializeJSON",
|
|
&Script_SerializeJSON);
|
|
Game::Lua::RegisterTableFunction("C_EncodingUtil", "DeserializeJSON",
|
|
&Script_DeserializeJSON);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
static const Game::ModuleAutoRegister _autoreg{&Register};
|
|
|
|
} // namespace Encoding::JSON
|