| @@ -0,0 +1,425 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "protocol.h" | |
| 24 | + | #include "connection.h" | |
| 25 | + | #include <framework/core/application.h> | |
| 26 | + | #include <random> | |
| 27 | + | ||
| 28 | + | #include <framework/net/packet_player.h> | |
| 29 | + | #include <framework/net/packet_recorder.h> | |
| 30 | + | ||
| 31 | + | extern asio::io_service g_ioService; | |
| 32 | + | ||
| 33 | + | Protocol::Protocol() | |
| 34 | + | { | |
| 35 | + | m_xteaEncryptionEnabled = false; | |
| 36 | + | m_checksumEnabled = false; | |
| 37 | + | m_sequencedPackets = false; | |
| 38 | + | m_bigPackets = false; | |
| 39 | + | m_compression = false; | |
| 40 | + | m_inputMessage = InputMessagePtr(new InputMessage); | |
| 41 | + | m_packetNumber = 0; | |
| 42 | + | ||
| 43 | + | // compression | |
| 44 | + | m_zstreamBuffer.resize(InputMessage::BUFFER_MAXSIZE); | |
| 45 | + | m_zstream.next_in = m_inputMessage->getDataBuffer(); | |
| 46 | + | m_zstream.next_out = m_zstreamBuffer.data(); | |
| 47 | + | m_zstream.avail_in = 0; | |
| 48 | + | m_zstream.avail_out = 0; | |
| 49 | + | m_zstream.total_in = 0; | |
| 50 | + | m_zstream.total_out = 0; | |
| 51 | + | m_zstream.zalloc = nullptr; | |
| 52 | + | m_zstream.zfree = nullptr; | |
| 53 | + | m_zstream.opaque = nullptr; | |
| 54 | + | m_zstream.data_type = Z_BINARY; | |
| 55 | + | inflateInit2(&m_zstream, -15); | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | Protocol::~Protocol() | |
| 59 | + | { | |
| 60 | + | VALIDATE(!g_app.isTerminated()); | |
| 61 | + | disconnect(); | |
| 62 | + | inflateEnd(&m_zstream); | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | void Protocol::connect(const std::string& host, uint16 port) | |
| 66 | + | { | |
| 67 | + | if (host == "proxy" || host == "0.0.0.0" || (host == "127.0.0.1" && g_proxy.isActive())) { | |
| 68 | + | m_disconnected = false; | |
| 69 | + | m_proxy = g_proxy.addSession(port, | |
| 70 | + | std::bind(&Protocol::onProxyPacket, asProtocol(), std::placeholders::_1), | |
| 71 | + | std::bind(&Protocol::onLocalDisconnected, asProtocol(), std::placeholders::_1)); | |
| 72 | + | return onConnect(); | |
| 73 | + | } | |
| 74 | + | m_connection = ConnectionPtr(new Connection); | |
| 75 | + | m_connection->setErrorCallback(std::bind(&Protocol::onError, asProtocol(), std::placeholders::_1)); | |
| 76 | + | m_connection->connect(host, port, std::bind(&Protocol::onConnect, asProtocol())); | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | void Protocol::disconnect() | |
| 80 | + | { | |
| 81 | + | m_disconnected = true; | |
| 82 | + | if (m_player) { | |
| 83 | + | m_player->stop(); | |
| 84 | + | return; | |
| 85 | + | } | |
| 86 | + | if (m_proxy) { | |
| 87 | + | g_proxy.removeSession(m_proxy); | |
| 88 | + | return; | |
| 89 | + | } | |
| 90 | + | if (m_connection) { | |
| 91 | + | m_connection->close(); | |
| 92 | + | m_connection.reset(); | |
| 93 | + | } | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | ||
| 97 | + | void Protocol::playRecord(PacketPlayerPtr player) | |
| 98 | + | { | |
| 99 | + | m_disconnected = false; | |
| 100 | + | m_player = player; | |
| 101 | + | m_player->start(std::bind(&Protocol::onPlayerPacket, asProtocol(), std::placeholders::_1), | |
| 102 | + | std::bind(&Protocol::onLocalDisconnected, asProtocol(), std::placeholders::_1)); | |
| 103 | + | return onConnect(); | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | void Protocol::setRecorder(PacketRecorderPtr recorder) | |
| 107 | + | { | |
| 108 | + | m_recorder = recorder; | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | bool Protocol::isConnected() | |
| 112 | + | { | |
| 113 | + | if (m_player) | |
| 114 | + | return !m_disconnected; | |
| 115 | + | if (m_proxy) | |
| 116 | + | return !m_disconnected; | |
| 117 | + | if (m_connection && m_connection->isConnected()) | |
| 118 | + | return true; | |
| 119 | + | return false; | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | bool Protocol::isConnecting() | |
| 123 | + | { | |
| 124 | + | if (m_player) | |
| 125 | + | return false; | |
| 126 | + | if (m_proxy) | |
| 127 | + | return false; | |
| 128 | + | if (m_connection && m_connection->isConnecting()) | |
| 129 | + | return true; | |
| 130 | + | return false; | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | void Protocol::send(const OutputMessagePtr& outputMessage, bool rawPacket) | |
| 134 | + | { | |
| 135 | + | if (m_player) { | |
| 136 | + | m_player->onOutputPacket(outputMessage); | |
| 137 | + | return; | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | if (m_recorder) { | |
| 141 | + | m_recorder->addOutputPacket(outputMessage); | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | if (!rawPacket) { | |
| 145 | + | // encrypt | |
| 146 | + | if (m_xteaEncryptionEnabled) | |
| 147 | + | xteaEncrypt(outputMessage); | |
| 148 | + | ||
| 149 | + | // write checksum | |
| 150 | + | if (m_sequencedPackets) | |
| 151 | + | outputMessage->writeSequence(m_packetNumber++); | |
| 152 | + | else if (m_checksumEnabled) | |
| 153 | + | outputMessage->writeChecksum(); | |
| 154 | + | ||
| 155 | + | // write message size | |
| 156 | + | outputMessage->writeMessageSize(m_bigPackets); | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | if (m_proxy) { | |
| 160 | + | auto packet = std::make_shared<ProxyPacket>(outputMessage->getHeaderBuffer(), outputMessage->getWriteBuffer()); | |
| 161 | + | g_proxy.send(m_proxy, packet); | |
| 162 | + | outputMessage->reset(); | |
| 163 | + | return; | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | // send | |
| 167 | + | if (m_connection) | |
| 168 | + | m_connection->write(outputMessage->getHeaderBuffer(), outputMessage->getMessageSize()); | |
| 169 | + | ||
| 170 | + | // reset message to allow reuse | |
| 171 | + | outputMessage->reset(); | |
| 172 | + | } | |
| 173 | + | ||
| 174 | + | void Protocol::recv() | |
| 175 | + | { | |
| 176 | + | if (m_player) | |
| 177 | + | return; | |
| 178 | + | ||
| 179 | + | if (m_proxy) { | |
| 180 | + | return; | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | m_inputMessage->reset(); | |
| 184 | + | ||
| 185 | + | // first update message header size | |
| 186 | + | int headerSize = m_bigPackets ? 4 : 2; // 2 or 4 bytes for message size | |
| 187 | + | if (m_checksumEnabled) | |
| 188 | + | headerSize += 4; // 4 bytes for checksum | |
| 189 | + | if (m_xteaEncryptionEnabled) | |
| 190 | + | headerSize += m_bigPackets ? 4 : 2; // 2 or 4 bytes for XTEA encrypted message size | |
| 191 | + | m_inputMessage->setHeaderSize(headerSize); | |
| 192 | + | ||
| 193 | + | // read the first 2 bytes which contain the message size | |
| 194 | + | if (m_connection) | |
| 195 | + | m_connection->read(m_bigPackets ? 4 : 2, std::bind(&Protocol::internalRecvHeader, asProtocol(), std::placeholders::_1, std::placeholders::_2)); | |
| 196 | + | } | |
| 197 | + | ||
| 198 | + | void Protocol::internalRecvHeader(uint8* buffer, uint32 size) | |
| 199 | + | { | |
| 200 | + | // read message size | |
| 201 | + | m_inputMessage->fillBuffer(buffer, size); | |
| 202 | + | uint32 remainingSize = m_inputMessage->readSize(m_bigPackets); | |
| 203 | + | ||
| 204 | + | // read remaining message data | |
| 205 | + | if (m_connection) | |
| 206 | + | m_connection->read(remainingSize, std::bind(&Protocol::internalRecvData, asProtocol(), std::placeholders::_1, std::placeholders::_2)); | |
| 207 | + | } | |
| 208 | + | ||
| 209 | + | void Protocol::internalRecvData(uint8* buffer, uint32 size) | |
| 210 | + | { | |
| 211 | + | // process data only if really connected | |
| 212 | + | if (!isConnected()) { | |
| 213 | + | g_logger.traceError("received data while disconnected"); | |
| 214 | + | return; | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | m_inputMessage->fillBuffer(buffer, size); | |
| 218 | + | ||
| 219 | + | bool decompress = false; | |
| 220 | + | if (m_sequencedPackets) { | |
| 221 | + | if (m_inputMessage->getU32() >= 0xC0000000) { | |
| 222 | + | decompress = true; | |
| 223 | + | } | |
| 224 | + | } else if (m_checksumEnabled) { | |
| 225 | + | if (m_inputMessage->peekU32() == 0) { // compressed data | |
| 226 | + | m_inputMessage->getU32(); | |
| 227 | + | decompress = true; | |
| 228 | + | } else if (!m_inputMessage->readChecksum()) { | |
| 229 | + | g_logger.traceError(stdext::format("got a network message with invalid checksum, size: %i", (int)m_inputMessage->getMessageSize())); | |
| 230 | + | return; | |
| 231 | + | } | |
| 232 | + | } | |
| 233 | + | ||
| 234 | + | if (m_xteaEncryptionEnabled) { | |
| 235 | + | if (!xteaDecrypt(m_inputMessage)) { | |
| 236 | + | g_logger.traceError("failed to decrypt message"); | |
| 237 | + | return; | |
| 238 | + | } | |
| 239 | + | } | |
| 240 | + | ||
| 241 | + | if (decompress || m_compression) { | |
| 242 | + | m_inputMessage->addZlibFooter(); | |
| 243 | + | m_zstream.next_in = m_inputMessage->getDataBuffer(); | |
| 244 | + | m_zstream.next_out = m_zstreamBuffer.data(); | |
| 245 | + | m_zstream.avail_in = m_inputMessage->getUnreadSize(); | |
| 246 | + | m_zstream.avail_out = m_zstreamBuffer.size(); | |
| 247 | + | if (inflate(&m_zstream, Z_SYNC_FLUSH) != Z_OK) { | |
| 248 | + | g_logger.traceError("failed to decompress message"); | |
| 249 | + | return; | |
| 250 | + | } | |
| 251 | + | int decryptedSize = m_zstreamBuffer.size() - m_zstream.avail_out; | |
| 252 | + | if (decryptedSize == 0) { | |
| 253 | + | g_logger.traceError(stdext::format("invalid size of decompressed message - %i", (int)decryptedSize)); | |
| 254 | + | return; | |
| 255 | + | } | |
| 256 | + | m_inputMessage->fillBuffer(m_zstreamBuffer.data(), decryptedSize); | |
| 257 | + | m_inputMessage->setMessageSize(m_inputMessage->getHeaderSize() + decryptedSize); | |
| 258 | + | } | |
| 259 | + | ||
| 260 | + | if (m_recorder) { | |
| 261 | + | m_recorder->addInputPacket(m_inputMessage); | |
| 262 | + | } | |
| 263 | + | onRecv(m_inputMessage); | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | void Protocol::generateXteaKey() | |
| 267 | + | { | |
| 268 | + | std::mt19937 eng(std::time(NULL)); | |
| 269 | + | std::uniform_int_distribution<uint32> unif(0, 0xFFFFFFFF); | |
| 270 | + | m_xteaKey[0] = unif(eng); | |
| 271 | + | m_xteaKey[1] = unif(eng); | |
| 272 | + | m_xteaKey[2] = unif(eng); | |
| 273 | + | m_xteaKey[3] = unif(eng); | |
| 274 | + | } | |
| 275 | + | ||
| 276 | + | void Protocol::setXteaKey(uint32 a, uint32 b, uint32 c, uint32 d) | |
| 277 | + | { | |
| 278 | + | m_xteaKey[0] = a; | |
| 279 | + | m_xteaKey[1] = b; | |
| 280 | + | m_xteaKey[2] = c; | |
| 281 | + | m_xteaKey[3] = d; | |
| 282 | + | } | |
| 283 | + | ||
| 284 | + | std::vector<uint32> Protocol::getXteaKey() | |
| 285 | + | { | |
| 286 | + | std::vector<uint32> xteaKey; | |
| 287 | + | xteaKey.resize(4); | |
| 288 | + | for (int i = 0; i < 4; ++i) | |
| 289 | + | xteaKey[i] = m_xteaKey[i]; | |
| 290 | + | return xteaKey; | |
| 291 | + | } | |
| 292 | + | ||
| 293 | + | bool Protocol::xteaDecrypt(const InputMessagePtr& inputMessage) | |
| 294 | + | { | |
| 295 | + | uint32 encryptedSize = inputMessage->getUnreadSize(); | |
| 296 | + | if (encryptedSize % 8 != 0) { | |
| 297 | + | g_logger.traceError(stdext::format("invalid encrypted network message %i", (int)encryptedSize)); | |
| 298 | + | return false; | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | uint32* buffer = (uint32*)(inputMessage->getReadBuffer()); | |
| 302 | + | uint32_t readPos = 0; | |
| 303 | + | ||
| 304 | + | while (readPos < encryptedSize / 4) { | |
| 305 | + | uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1]; | |
| 306 | + | uint32 delta = 0x61C88647; | |
| 307 | + | uint32 sum = 0xC6EF3720; | |
| 308 | + | ||
| 309 | + | for (int32 i = 0; i < 32; i++) { | |
| 310 | + | v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum >> 11 & 3]); | |
| 311 | + | sum += delta; | |
| 312 | + | v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]); | |
| 313 | + | } | |
| 314 | + | buffer[readPos] = v0; buffer[readPos + 1] = v1; | |
| 315 | + | readPos = readPos + 2; | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | uint32 decryptedSize = m_bigPackets ? (inputMessage->getU32() + 4) : (inputMessage->getU16() + 2); | |
| 319 | + | int sizeDelta = decryptedSize - encryptedSize; | |
| 320 | + | if (sizeDelta > 0 || -sizeDelta > (int)encryptedSize) { | |
| 321 | + | g_logger.traceError("invalid decrypted network message"); | |
| 322 | + | return false; | |
| 323 | + | } | |
| 324 | + | ||
| 325 | + | inputMessage->setMessageSize(inputMessage->getMessageSize() + sizeDelta); | |
| 326 | + | return true; | |
| 327 | + | } | |
| 328 | + | ||
| 329 | + | void Protocol::xteaEncrypt(const OutputMessagePtr& outputMessage) | |
| 330 | + | { | |
| 331 | + | outputMessage->writeMessageSize(m_bigPackets); | |
| 332 | + | uint32 encryptedSize = outputMessage->getMessageSize(); | |
| 333 | + | ||
| 334 | + | //add bytes until reach 8 multiple | |
| 335 | + | if ((encryptedSize % 8) != 0) { | |
| 336 | + | uint32 n = 8 - (encryptedSize % 8); | |
| 337 | + | outputMessage->addPaddingBytes(n); | |
| 338 | + | encryptedSize += n; | |
| 339 | + | } | |
| 340 | + | ||
| 341 | + | uint32_t readPos = 0; | |
| 342 | + | uint32* buffer = (uint32*)(outputMessage->getDataBuffer() - (m_bigPackets ? 4 : 2)); | |
| 343 | + | while (readPos < encryptedSize / 4) { | |
| 344 | + | uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1]; | |
| 345 | + | uint32 delta = 0x61C88647; | |
| 346 | + | uint32 sum = 0; | |
| 347 | + | ||
| 348 | + | for (int32 i = 0; i < 32; i++) { | |
| 349 | + | v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]); | |
| 350 | + | sum -= delta; | |
| 351 | + | v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum >> 11 & 3]); | |
| 352 | + | } | |
| 353 | + | buffer[readPos] = v0; buffer[readPos + 1] = v1; | |
| 354 | + | readPos = readPos + 2; | |
| 355 | + | } | |
| 356 | + | } | |
| 357 | + | ||
| 358 | + | void Protocol::onConnect() | |
| 359 | + | { | |
| 360 | + | callLuaField("onConnect"); | |
| 361 | + | } | |
| 362 | + | ||
| 363 | + | void Protocol::onRecv(const InputMessagePtr& inputMessage) | |
| 364 | + | { | |
| 365 | + | callLuaField("onRecv", inputMessage); | |
| 366 | + | } | |
| 367 | + | ||
| 368 | + | void Protocol::onError(const boost::system::error_code& err) | |
| 369 | + | { | |
| 370 | + | callLuaField("onError", err.message(), err.value()); | |
| 371 | + | disconnect(); | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | void Protocol::onPlayerPacket(const std::shared_ptr<std::vector<uint8_t>>& packet) | |
| 375 | + | { | |
| 376 | + | if (m_disconnected) | |
| 377 | + | return; | |
| 378 | + | auto self(asProtocol()); | |
| 379 | + | boost::asio::post(g_ioService, [&, self, packet] { | |
| 380 | + | if (m_disconnected) | |
| 381 | + | return; | |
| 382 | + | m_inputMessage->reset(); | |
| 383 | + | ||
| 384 | + | m_inputMessage->setHeaderSize(0); | |
| 385 | + | m_inputMessage->fillBuffer(packet->data(), packet->size()); | |
| 386 | + | m_inputMessage->setMessageSize(packet->size()); | |
| 387 | + | onRecv(m_inputMessage); | |
| 388 | + | }); | |
| 389 | + | } | |
| 390 | + | ||
| 391 | + | void Protocol::onProxyPacket(const std::shared_ptr<std::vector<uint8_t>>& packet) | |
| 392 | + | { | |
| 393 | + | if (m_disconnected) | |
| 394 | + | return; | |
| 395 | + | auto self(asProtocol()); | |
| 396 | + | boost::asio::post(g_ioService, [&, self, packet] { | |
| 397 | + | if (m_disconnected) | |
| 398 | + | return; | |
| 399 | + | m_inputMessage->reset(); | |
| 400 | + | ||
| 401 | + | // first update message header size | |
| 402 | + | int headerSize = m_bigPackets ? 4 : 2; // 2 bytes for message size | |
| 403 | + | if (m_checksumEnabled) | |
| 404 | + | headerSize += 4; // 4 bytes for checksum | |
| 405 | + | if (m_xteaEncryptionEnabled) | |
| 406 | + | headerSize += m_bigPackets ? 4 : 2; // 2 bytes for XTEA encrypted message size | |
| 407 | + | m_inputMessage->setHeaderSize(headerSize); | |
| 408 | + | m_inputMessage->fillBuffer(packet->data(), m_bigPackets ? 4 : 2); | |
| 409 | + | m_inputMessage->readSize(m_bigPackets); | |
| 410 | + | internalRecvData(packet->data() + (m_bigPackets ? 4 : 2), packet->size() - (m_bigPackets ? 4 : 2)); | |
| 411 | + | }); | |
| 412 | + | } | |
| 413 | + | ||
| 414 | + | void Protocol::onLocalDisconnected(boost::system::error_code ec) | |
| 415 | + | { | |
| 416 | + | if (m_disconnected) | |
| 417 | + | return; | |
| 418 | + | auto self(asProtocol()); | |
| 419 | + | boost::asio::post(g_ioService, [&, self, ec] { | |
| 420 | + | if (m_disconnected) | |
| 421 | + | return; | |
| 422 | + | m_disconnected = true; | |
| 423 | + | onError(ec); | |
| 424 | + | }); | |
| 425 | + | } |
| @@ -0,0 +1,107 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef PROTOCOL_H | |
| 24 | + | #define PROTOCOL_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include "inputmessage.h" | |
| 28 | + | #include "outputmessage.h" | |
| 29 | + | #include "connection.h" | |
| 30 | + | ||
| 31 | + | #include <framework/luaengine/luaobject.h> | |
| 32 | + | #include <framework/proxy/proxy.h> | |
| 33 | + | ||
| 34 | + | #include <zlib.h> | |
| 35 | + | ||
| 36 | + | // @bindclass | |
| 37 | + | class Protocol : public LuaObject | |
| 38 | + | { | |
| 39 | + | public: | |
| 40 | + | Protocol(); | |
| 41 | + | virtual ~Protocol(); | |
| 42 | + | ||
| 43 | + | ||
| 44 | + | void connect(const std::string& host, uint16 port); | |
| 45 | + | void disconnect(); | |
| 46 | + | ||
| 47 | + | void setRecorder(PacketRecorderPtr recorder); | |
| 48 | + | void playRecord(PacketPlayerPtr player); | |
| 49 | + | ||
| 50 | + | bool isConnected(); | |
| 51 | + | bool isConnecting(); | |
| 52 | + | ticks_t getElapsedTicksSinceLastRead() { return m_connection ? m_connection->getElapsedTicksSinceLastRead() : -1; } | |
| 53 | + | ||
| 54 | + | ConnectionPtr getConnection() { return m_connection; } | |
| 55 | + | void setConnection(const ConnectionPtr& connection) { m_connection = connection; } | |
| 56 | + | ||
| 57 | + | void generateXteaKey(); | |
| 58 | + | void setXteaKey(uint32 a, uint32 b, uint32 c, uint32 d); | |
| 59 | + | std::vector<uint32> getXteaKey(); | |
| 60 | + | void enableXteaEncryption() { m_xteaEncryptionEnabled = true; } | |
| 61 | + | ||
| 62 | + | void enableChecksum() { m_checksumEnabled = true; } | |
| 63 | + | void enabledSequencedPackets() { m_sequencedPackets = true; } | |
| 64 | + | void enableBigPackets() { m_bigPackets = true; } | |
| 65 | + | void enableCompression() { m_compression = true; } | |
| 66 | + | ||
| 67 | + | virtual void send(const OutputMessagePtr& outputMessage, bool rawPacket = false); | |
| 68 | + | virtual void recv(); | |
| 69 | + | ||
| 70 | + | ProtocolPtr asProtocol() { return static_self_cast<Protocol>(); } | |
| 71 | + | ||
| 72 | + | protected: | |
| 73 | + | virtual void onConnect(); | |
| 74 | + | virtual void onRecv(const InputMessagePtr& inputMessage); | |
| 75 | + | virtual void onError(const boost::system::error_code& err); | |
| 76 | + | ||
| 77 | + | void onProxyPacket(const std::shared_ptr<std::vector<uint8_t>>& packet); | |
| 78 | + | void onPlayerPacket(const std::shared_ptr<std::vector<uint8_t>>& packet); | |
| 79 | + | void onLocalDisconnected(boost::system::error_code ec); | |
| 80 | + | bool m_disconnected = false; | |
| 81 | + | uint32_t m_proxy = 0; | |
| 82 | + | ||
| 83 | + | uint32 m_xteaKey[4]; | |
| 84 | + | uint32 m_packetNumber; | |
| 85 | + | ||
| 86 | + | PacketPlayerPtr m_player; | |
| 87 | + | PacketRecorderPtr m_recorder; | |
| 88 | + | ||
| 89 | + | private: | |
| 90 | + | void internalRecvHeader(uint8* buffer, uint32 size); | |
| 91 | + | void internalRecvData(uint8* buffer, uint32 size); | |
| 92 | + | ||
| 93 | + | bool xteaDecrypt(const InputMessagePtr& inputMessage); | |
| 94 | + | void xteaEncrypt(const OutputMessagePtr& outputMessage); | |
| 95 | + | ||
| 96 | + | bool m_checksumEnabled; | |
| 97 | + | bool m_sequencedPackets; | |
| 98 | + | bool m_xteaEncryptionEnabled; | |
| 99 | + | bool m_bigPackets; | |
| 100 | + | bool m_compression; | |
| 101 | + | ConnectionPtr m_connection; | |
| 102 | + | InputMessagePtr m_inputMessage; | |
| 103 | + | z_stream m_zstream; | |
| 104 | + | std::vector<uint8_t> m_zstreamBuffer; | |
| 105 | + | }; | |
| 106 | + | ||
| 107 | + | #endif |
| @@ -0,0 +1,64 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "server.h" | |
| 24 | + | #include "connection.h" | |
| 25 | + | ||
| 26 | + | extern asio::io_service g_ioService; | |
| 27 | + | ||
| 28 | + | Server::Server(int port) | |
| 29 | + | : m_acceptor(g_ioService, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)) | |
| 30 | + | { | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | ServerPtr Server::create(int port) | |
| 34 | + | { | |
| 35 | + | try { | |
| 36 | + | Server *server = new Server(port); | |
| 37 | + | return ServerPtr(server); | |
| 38 | + | } | |
| 39 | + | catch(const std::exception& e) { | |
| 40 | + | g_logger.error(stdext::format("Failed to initialize server: %s", e.what())); | |
| 41 | + | return ServerPtr(); | |
| 42 | + | } | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | void Server::close() | |
| 46 | + | { | |
| 47 | + | m_isOpen = false; | |
| 48 | + | m_acceptor.cancel(); | |
| 49 | + | m_acceptor.close(); | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | void Server::acceptNext() | |
| 53 | + | { | |
| 54 | + | ConnectionPtr connection = ConnectionPtr(new Connection); | |
| 55 | + | connection->m_connecting = true; | |
| 56 | + | auto self = static_self_cast<Server>(); | |
| 57 | + | m_acceptor.async_accept(connection->m_socket, [=](const boost::system::error_code& error) { | |
| 58 | + | if(!error) { | |
| 59 | + | connection->m_connected = true; | |
| 60 | + | connection->m_connecting = false; | |
| 61 | + | } | |
| 62 | + | self->callLuaField("onAccept", connection, error.message(), error.value()); | |
| 63 | + | }); | |
| 64 | + | } |
| @@ -0,0 +1,44 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef SERVER_H | |
| 24 | + | #define SERVER_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include <framework/luaengine/luaobject.h> | |
| 28 | + | ||
| 29 | + | class Server : public LuaObject | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | Server(int port); | |
| 33 | + | static ServerPtr create(int port); | |
| 34 | + | bool isOpen() { return m_isOpen; } | |
| 35 | + | void close(); | |
| 36 | + | ||
| 37 | + | void acceptNext(); | |
| 38 | + | ||
| 39 | + | private: | |
| 40 | + | stdext::boolean<true> m_isOpen; | |
| 41 | + | asio::ip::tcp::acceptor m_acceptor; | |
| 42 | + | }; | |
| 43 | + | ||
| 44 | + | #endif |
| @@ -0,0 +1,37 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef FRAMEWORK_OTML_DECLARATIONS_H | |
| 24 | + | #define FRAMEWORK_OTML_DECLARATIONS_H | |
| 25 | + | ||
| 26 | + | #include <framework/global.h> | |
| 27 | + | ||
| 28 | + | class OTMLNode; | |
| 29 | + | class OTMLDocument; | |
| 30 | + | class OTMLParser; | |
| 31 | + | class OTMLEmitter; | |
| 32 | + | ||
| 33 | + | typedef stdext::shared_object_ptr<OTMLNode> OTMLNodePtr; | |
| 34 | + | typedef stdext::shared_object_ptr<OTMLDocument> OTMLDocumentPtr; | |
| 35 | + | typedef std::vector<OTMLNodePtr> OTMLNodeList; | |
| 36 | + | ||
| 37 | + | #endif |
| @@ -0,0 +1,29 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef OTML_H | |
| 24 | + | #define OTML_H | |
| 25 | + | ||
| 26 | + | #include "otmldocument.h" | |
| 27 | + | #include "otmlnode.h" | |
| 28 | + | ||
| 29 | + | #endif |
| @@ -0,0 +1,69 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "otmldocument.h" | |
| 24 | + | #include "otmlparser.h" | |
| 25 | + | #include "otmlemitter.h" | |
| 26 | + | ||
| 27 | + | #include <framework/core/resourcemanager.h> | |
| 28 | + | ||
| 29 | + | OTMLDocumentPtr OTMLDocument::create() | |
| 30 | + | { | |
| 31 | + | OTMLDocumentPtr doc(new OTMLDocument); | |
| 32 | + | doc->setTag("doc"); | |
| 33 | + | return doc; | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | OTMLDocumentPtr OTMLDocument::parse(const std::string& fileName) | |
| 37 | + | { | |
| 38 | + | std::stringstream fin; | |
| 39 | + | std::string source = g_resources.resolvePath(fileName); | |
| 40 | + | g_resources.readFileStream(source, fin); | |
| 41 | + | return parse(fin, source); | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | OTMLDocumentPtr OTMLDocument::parseString(const std::string& data, const std::string& source) | |
| 45 | + | { | |
| 46 | + | std::istringstream is(data); | |
| 47 | + | return parse(is, source); | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | OTMLDocumentPtr OTMLDocument::parse(std::istream& in, const std::string& source) | |
| 51 | + | { | |
| 52 | + | OTMLDocumentPtr doc(new OTMLDocument); | |
| 53 | + | doc->setSource(source); | |
| 54 | + | OTMLParser parser(doc, in); | |
| 55 | + | parser.parse(); | |
| 56 | + | return doc; | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | std::string OTMLDocument::emit() | |
| 60 | + | { | |
| 61 | + | return OTMLEmitter::emitNode(asOTMLNode()) + "\n"; | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | bool OTMLDocument::save(const std::string& fileName) | |
| 65 | + | { | |
| 66 | + | m_source = fileName; | |
| 67 | + | return g_resources.writeFileContents(fileName, emit()); | |
| 68 | + | } | |
| 69 | + |
| @@ -0,0 +1,56 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef OTMLDOCUMENT_H | |
| 24 | + | #define OTMLDOCUMENT_H | |
| 25 | + | ||
| 26 | + | #include "otmlnode.h" | |
| 27 | + | ||
| 28 | + | class OTMLDocument : public OTMLNode | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | virtual ~OTMLDocument() { } | |
| 32 | + | ||
| 33 | + | /// Create a new OTML document for filling it with nodes | |
| 34 | + | static OTMLDocumentPtr create(); | |
| 35 | + | ||
| 36 | + | /// Parse OTML from a file | |
| 37 | + | static OTMLDocumentPtr parse(const std::string& fileName); | |
| 38 | + | ||
| 39 | + | /// Parse OTML from a string | |
| 40 | + | static OTMLDocumentPtr parseString(const std::string& data, const std::string& source); | |
| 41 | + | ||
| 42 | + | /// Parse OTML from input stream | |
| 43 | + | /// @param source is the file name that will be used to show errors messages | |
| 44 | + | static OTMLDocumentPtr parse(std::istream& in, const std::string& source); | |
| 45 | + | ||
| 46 | + | /// Emits this document and all it's children to a std::string | |
| 47 | + | std::string emit(); | |
| 48 | + | ||
| 49 | + | /// Save this document to a file | |
| 50 | + | bool save(const std::string& fileName); | |
| 51 | + | ||
| 52 | + | private: | |
| 53 | + | OTMLDocument() { } | |
| 54 | + | }; | |
| 55 | + | ||
| 56 | + | #endif |
| @@ -0,0 +1,94 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "otmlemitter.h" | |
| 24 | + | #include "otmldocument.h" | |
| 25 | + | ||
| 26 | + | std::string OTMLEmitter::emitNode(const OTMLNodePtr& node, int currentDepth) | |
| 27 | + | { | |
| 28 | + | std::stringstream ss; | |
| 29 | + | ||
| 30 | + | // emit nodes | |
| 31 | + | if(currentDepth >= 0) { | |
| 32 | + | // fill spaces for current depth | |
| 33 | + | for(int i=0;i<currentDepth;++i) | |
| 34 | + | ss << " "; | |
| 35 | + | ||
| 36 | + | // emit node tag | |
| 37 | + | if(node->hasTag()) { | |
| 38 | + | ss << node->tag(); | |
| 39 | + | ||
| 40 | + | // add ':' to if the node is unique or has value | |
| 41 | + | if(node->hasValue() || node->isUnique() || node->isNull()) | |
| 42 | + | ss << ":"; | |
| 43 | + | } else | |
| 44 | + | ss << "-"; | |
| 45 | + | ||
| 46 | + | // emit node value | |
| 47 | + | if(node->isNull()) | |
| 48 | + | ss << " ~"; | |
| 49 | + | else if(node->hasValue()) { | |
| 50 | + | ss << " "; | |
| 51 | + | ||
| 52 | + | std::string value = node->value(); | |
| 53 | + | ||
| 54 | + | // emit multiline values | |
| 55 | + | if(value.find("\n") != std::string::npos) { | |
| 56 | + | if(value[value.length()-1] == '\n' && value[value.length()-2] == '\n') | |
| 57 | + | ss << "|+"; | |
| 58 | + | else if(value[value.length()-1] == '\n') | |
| 59 | + | ss << "|"; | |
| 60 | + | else | |
| 61 | + | ss << "|-"; | |
| 62 | + | ||
| 63 | + | // multilines | |
| 64 | + | for(std::size_t pos = 0; pos < value.length(); ++pos) { | |
| 65 | + | ss << "\n"; | |
| 66 | + | ||
| 67 | + | // fill spaces for multiline depth | |
| 68 | + | for(int i=0;i<currentDepth+1;++i) | |
| 69 | + | ss << " "; | |
| 70 | + | ||
| 71 | + | // fill until a new line | |
| 72 | + | while(pos < value.length()) { | |
| 73 | + | if(value[pos] == '\n') | |
| 74 | + | break; | |
| 75 | + | ss << value[pos++]; | |
| 76 | + | } | |
| 77 | + | } | |
| 78 | + | // emit inline values | |
| 79 | + | } else | |
| 80 | + | ss << value; | |
| 81 | + | } | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | // emit children | |
| 85 | + | int i = 0; | |
| 86 | + | for(auto& child : node->children()) { | |
| 87 | + | if(currentDepth >= 0 || i != 0) | |
| 88 | + | ss << "\n"; | |
| 89 | + | ss << emitNode(child, currentDepth+1); | |
| 90 | + | i += 1; | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | return ss.str(); | |
| 94 | + | } |
| @@ -0,0 +1,35 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef OTMLEMITTER_H | |
| 24 | + | #define OTMLEMITTER_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | ||
| 28 | + | class OTMLEmitter | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | /// Emits a node and it's children to a std::string | |
| 32 | + | static std::string emitNode(const OTMLNodePtr& node, int currentDepth = -1); | |
| 33 | + | }; | |
| 34 | + | ||
| 35 | + | #endif |
| @@ -0,0 +1,47 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "otmlexception.h" | |
| 24 | + | #include "otmldocument.h" | |
| 25 | + | ||
| 26 | + | OTMLException::OTMLException(const OTMLNodePtr& node, const std::string& error) | |
| 27 | + | { | |
| 28 | + | std::stringstream ss; | |
| 29 | + | ss << "OTML error"; | |
| 30 | + | if(!node->source().empty()) | |
| 31 | + | ss << " in '" << node->source() << "'"; | |
| 32 | + | ss << ": " << error; | |
| 33 | + | m_what = ss.str(); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | OTMLException::OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line) | |
| 37 | + | { | |
| 38 | + | std::stringstream ss; | |
| 39 | + | ss << "OTML error"; | |
| 40 | + | if(doc && !doc->source().empty()) { | |
| 41 | + | ss << " in '" << doc->source() << "'"; | |
| 42 | + | if(line >= 0) | |
| 43 | + | ss << " at line " << line; | |
| 44 | + | } | |
| 45 | + | ss << ": " << error; | |
| 46 | + | m_what = ss.str(); | |
| 47 | + | } |
| @@ -0,0 +1,42 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef OTMLEXCEPTION_H | |
| 24 | + | #define OTMLEXCEPTION_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | ||
| 28 | + | /// All OTML errors throw this exception | |
| 29 | + | class OTMLException : public stdext::exception | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | OTMLException(const OTMLNodePtr& node, const std::string& error); | |
| 33 | + | OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line = -1); | |
| 34 | + | virtual ~OTMLException() throw() { }; | |
| 35 | + | ||
| 36 | + | virtual const char* what() const throw() { return m_what.c_str(); } | |
| 37 | + | ||
| 38 | + | protected: | |
| 39 | + | std::string m_what; | |
| 40 | + | }; | |
| 41 | + | ||
| 42 | + | #endif |
| @@ -0,0 +1,204 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "otmlnode.h" | |
| 24 | + | #include "otmlemitter.h" | |
| 25 | + | #include "otmldocument.h" | |
| 26 | + | ||
| 27 | + | #include <framework/util/extras.h> | |
| 28 | + | ||
| 29 | + | OTMLNodePtr OTMLNode::create(std::string tag, bool unique) | |
| 30 | + | { | |
| 31 | + | OTMLNodePtr node(new OTMLNode); | |
| 32 | + | node->setTag(tag); | |
| 33 | + | node->setUnique(unique); | |
| 34 | + | return node; | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | OTMLNodePtr OTMLNode::create(std::string tag, std::string value) | |
| 38 | + | { | |
| 39 | + | OTMLNodePtr node(new OTMLNode); | |
| 40 | + | node->setTag(tag); | |
| 41 | + | node->setValue(value); | |
| 42 | + | node->setUnique(true); | |
| 43 | + | return node; | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | bool OTMLNode::hasChildren() | |
| 47 | + | { | |
| 48 | + | for (auto& [tag, children] : m_children) { | |
| 49 | + | for (auto& child : children) { | |
| 50 | + | if (!child->isNull()) | |
| 51 | + | return true; | |
| 52 | + | } | |
| 53 | + | } | |
| 54 | + | return false; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | OTMLNodePtr OTMLNode::get(const std::string& childTag) | |
| 58 | + | { | |
| 59 | + | if (childTag.size() > 0 && childTag[0] == '!') | |
| 60 | + | g_logger.fatal(stdext::format("Invalid childTag %s", childTag)); | |
| 61 | + | ||
| 62 | + | auto it = m_children.find(childTag); | |
| 63 | + | if (it == m_children.end()) | |
| 64 | + | return nullptr; | |
| 65 | + | ||
| 66 | + | for (auto& child : it->second) { | |
| 67 | + | if (!child->isNull()) | |
| 68 | + | return child; | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | return nullptr; | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | OTMLNodePtr OTMLNode::at(const std::string& childTag) | |
| 75 | + | { | |
| 76 | + | OTMLNodePtr res = get(childTag); | |
| 77 | + | if(!res) | |
| 78 | + | throw OTMLException(asOTMLNode(), stdext::format("child node with tag '%s' not found", childTag)); | |
| 79 | + | return res; | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | void OTMLNode::addChild(const OTMLNodePtr& newChild) | |
| 83 | + | { | |
| 84 | + | // replace is needed when the tag is marked as unique | |
| 85 | + | if(newChild->hasTag()) { | |
| 86 | + | auto it = m_children.find(newChild->tag()); | |
| 87 | + | if (it != m_children.end()) { | |
| 88 | + | for (auto& node : it->second) { | |
| 89 | + | if (!node->isUnique() && !newChild->isUnique()) | |
| 90 | + | continue; | |
| 91 | + | newChild->setUnique(true); | |
| 92 | + | ||
| 93 | + | if (node->hasChildren() && newChild->hasChildren()) { | |
| 94 | + | OTMLNodePtr tmpNode = node->clone(); | |
| 95 | + | tmpNode->merge(newChild); | |
| 96 | + | newChild->copy(tmpNode); | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | it->second.clear(); | |
| 100 | + | break; | |
| 101 | + | } | |
| 102 | + | } | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | static size_t index = 0; | |
| 106 | + | if(newChild->getIndex() == 0) | |
| 107 | + | newChild->setIndex(++index); | |
| 108 | + | m_children[newChild->tag()].push_back(newChild); | |
| 109 | + | newChild->lockTag(); | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | bool OTMLNode::removeChild(const OTMLNodePtr& oldChild) | |
| 113 | + | { | |
| 114 | + | auto it = m_children.find(oldChild->tag()); | |
| 115 | + | if (it == m_children.end()) | |
| 116 | + | return false; | |
| 117 | + | ||
| 118 | + | auto it2 = std::find(it->second.begin(), it->second.end(), oldChild); | |
| 119 | + | if(it2 != it->second.end()) { | |
| 120 | + | it->second.erase(it2); | |
| 121 | + | return true; | |
| 122 | + | } | |
| 123 | + | return false; | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | void OTMLNode::copy(const OTMLNodePtr& node) | |
| 127 | + | { | |
| 128 | + | setTag(node->tag()); | |
| 129 | + | setValue(node->rawValue()); | |
| 130 | + | setUnique(node->isUnique()); | |
| 131 | + | setNull(node->isNull()); | |
| 132 | + | setSource(node->source()); | |
| 133 | + | setIndex(node->getIndex()); | |
| 134 | + | clear(); | |
| 135 | + | for (auto& [tag, children] : node->m_children) { | |
| 136 | + | for (auto& child : children) { | |
| 137 | + | addChild(child->clone()); | |
| 138 | + | } | |
| 139 | + | } | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | void OTMLNode::merge(const OTMLNodePtr& node) | |
| 143 | + | { | |
| 144 | + | for (auto& [tag, children] : node->m_children) { | |
| 145 | + | for (auto& child : children) { | |
| 146 | + | addChild(child->clone()); | |
| 147 | + | } | |
| 148 | + | } | |
| 149 | + | setTag(node->tag()); | |
| 150 | + | setSource(node->source()); | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | void OTMLNode::clear() | |
| 154 | + | { | |
| 155 | + | m_children.clear(); | |
| 156 | + | } | |
| 157 | + | ||
| 158 | + | OTMLNodeList OTMLNode::children() | |
| 159 | + | { | |
| 160 | + | OTMLNodeList ret; | |
| 161 | + | for (auto& [tag, children] : m_children) { | |
| 162 | + | for (auto& child : children) { | |
| 163 | + | if (!child->isNull()) | |
| 164 | + | ret.push_back(child); | |
| 165 | + | } | |
| 166 | + | } | |
| 167 | + | std::sort(ret.begin(), ret.end(), [](auto& n1, auto& n2) { | |
| 168 | + | return n1->getIndex() < n2->getIndex(); | |
| 169 | + | }); | |
| 170 | + | return ret; | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | OTMLNodePtr OTMLNode::clone() | |
| 174 | + | { | |
| 175 | + | OTMLNodePtr myClone(new OTMLNode); | |
| 176 | + | myClone->setTag(m_tag); | |
| 177 | + | myClone->setValue(m_value); | |
| 178 | + | myClone->setUnique(m_unique); | |
| 179 | + | myClone->setNull(m_null); | |
| 180 | + | myClone->setSource(m_source); | |
| 181 | + | myClone->setIndex(m_index); | |
| 182 | + | for (auto& [tag, children] : m_children) { | |
| 183 | + | for (auto& child : children) { | |
| 184 | + | myClone->addChild(child->clone()); | |
| 185 | + | } | |
| 186 | + | } | |
| 187 | + | return myClone; | |
| 188 | + | } | |
| 189 | + | ||
| 190 | + | std::string OTMLNode::emit() | |
| 191 | + | { | |
| 192 | + | return OTMLEmitter::emitNode(asOTMLNode(), 0); | |
| 193 | + | } | |
| 194 | + | ||
| 195 | + | void OTMLNode::setTag(const std::string& tag) { | |
| 196 | + | if (m_tagLocked && tag != m_tag) { | |
| 197 | + | std::string correct_tag = m_tag; | |
| 198 | + | if (correct_tag.size() > 0 && m_tag[0] == '!') | |
| 199 | + | correct_tag = correct_tag.substr(1); | |
| 200 | + | if(correct_tag != tag) | |
| 201 | + | g_logger.fatal(stdext::format("Trying to setTag for locked QTMLNode %s to %s", m_tag, tag)); | |
| 202 | + | } | |
| 203 | + | m_tag = tag; | |
| 204 | + | } |
| @@ -0,0 +1,176 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef OTMLNODE_H | |
| 24 | + | #define OTMLNODE_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | ||
| 28 | + | class OTMLNode : public stdext::shared_object | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | virtual ~OTMLNode() { } | |
| 32 | + | ||
| 33 | + | static OTMLNodePtr create(std::string tag = "", bool unique = false); | |
| 34 | + | static OTMLNodePtr create(std::string tag, std::string value); | |
| 35 | + | ||
| 36 | + | std::string tag() { return m_tag; } | |
| 37 | + | int size() { return m_children.size(); } | |
| 38 | + | std::string source() { return m_source; } | |
| 39 | + | std::string rawValue() { return m_value; } | |
| 40 | + | ||
| 41 | + | bool isUnique() { return m_unique; } | |
| 42 | + | bool isNull() { return m_null; } | |
| 43 | + | ||
| 44 | + | bool hasTag() { return !m_tag.empty(); } | |
| 45 | + | bool hasValue() { return !m_value.empty(); } | |
| 46 | + | bool hasChildren(); | |
| 47 | + | bool hasChildAt(const std::string& childTag) { return !!get(childTag); } | |
| 48 | + | size_t getIndex() { return m_index; } | |
| 49 | + | ||
| 50 | + | void setTag(const std::string& tag); | |
| 51 | + | void setValue(const std::string& value) { m_value = value; } | |
| 52 | + | void setNull(bool null) { m_null = null; } | |
| 53 | + | void setUnique(bool unique) { m_unique = unique; } | |
| 54 | + | void setSource(const std::string& source) { m_source = source; } | |
| 55 | + | void setIndex(size_t index) { m_index = index; } | |
| 56 | + | ||
| 57 | + | void lockTag() { | |
| 58 | + | m_tagLocked = true; | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | OTMLNodePtr get(const std::string& childTag); | |
| 62 | + | OTMLNodePtr getIndex(int childIndex); | |
| 63 | + | ||
| 64 | + | OTMLNodePtr at(const std::string& childTag); | |
| 65 | + | ||
| 66 | + | void addChild(const OTMLNodePtr& newChild); | |
| 67 | + | bool removeChild(const OTMLNodePtr& oldChild); | |
| 68 | + | void copy(const OTMLNodePtr& node); | |
| 69 | + | void merge(const OTMLNodePtr& node); | |
| 70 | + | void clear(); | |
| 71 | + | ||
| 72 | + | OTMLNodeList children(); | |
| 73 | + | OTMLNodePtr clone(); | |
| 74 | + | ||
| 75 | + | template<typename T = std::string> | |
| 76 | + | T value(); | |
| 77 | + | template<typename T = std::string> | |
| 78 | + | T valueAt(const std::string& childTag); | |
| 79 | + | template<typename T = std::string> | |
| 80 | + | T valueAtIndex(int childIndex); | |
| 81 | + | template<typename T = std::string> | |
| 82 | + | T valueAt(const std::string& childTag, const T& def); | |
| 83 | + | template<typename T = std::string> | |
| 84 | + | T valueAtIndex(int childIndex, const T& def); | |
| 85 | + | ||
| 86 | + | template<typename T> | |
| 87 | + | void write(const T& v); | |
| 88 | + | template<typename T> | |
| 89 | + | void writeAt(const std::string& childTag, const T& v); | |
| 90 | + | template<typename T> | |
| 91 | + | void writeIn(const T& v); | |
| 92 | + | ||
| 93 | + | virtual std::string emit(); | |
| 94 | + | ||
| 95 | + | OTMLNodePtr asOTMLNode() { return static_self_cast<OTMLNode>(); } | |
| 96 | + | ||
| 97 | + | protected: | |
| 98 | + | OTMLNode() : m_unique(false), m_null(false) { } | |
| 99 | + | ||
| 100 | + | std::unordered_map<std::string, std::vector<OTMLNodePtr>> m_children; | |
| 101 | + | std::string m_tag; | |
| 102 | + | std::string m_value; | |
| 103 | + | std::string m_source; | |
| 104 | + | size_t m_index = 0; | |
| 105 | + | bool m_unique; | |
| 106 | + | bool m_null; | |
| 107 | + | bool m_tagLocked = false; | |
| 108 | + | }; | |
| 109 | + | ||
| 110 | + | #include "otmlexception.h" | |
| 111 | + | ||
| 112 | + | template<> | |
| 113 | + | inline std::string OTMLNode::value<std::string>() { | |
| 114 | + | std::string value = m_value; | |
| 115 | + | if(stdext::starts_with(value, "\"") && stdext::ends_with(value, "\"")) { | |
| 116 | + | value = value.substr(1, value.length()-2); | |
| 117 | + | stdext::replace_all(value, "\\\\", "\\"); | |
| 118 | + | stdext::replace_all(value, "\\\"", "\""); | |
| 119 | + | stdext::replace_all(value, "\\t", "\t"); | |
| 120 | + | stdext::replace_all(value, "\\n", "\n"); | |
| 121 | + | stdext::replace_all(value, "\\'", "\'"); | |
| 122 | + | } | |
| 123 | + | return value; | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | template<typename T> | |
| 127 | + | T OTMLNode::value() { | |
| 128 | + | T ret; | |
| 129 | + | if(!stdext::cast(m_value, ret)) | |
| 130 | + | throw OTMLException(asOTMLNode(), stdext::format("failed to cast node value '%s' to type '%s'", m_value, stdext::demangle_type<T>())); | |
| 131 | + | return ret; | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | template<typename T> | |
| 135 | + | T OTMLNode::valueAt(const std::string& childTag) { | |
| 136 | + | OTMLNodePtr node = at(childTag); | |
| 137 | + | return node->value<T>(); | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | template<typename T> | |
| 141 | + | T OTMLNode::valueAt(const std::string& childTag, const T& def) { | |
| 142 | + | if(OTMLNodePtr node = get(childTag)) | |
| 143 | + | if(!node->isNull()) | |
| 144 | + | return node->value<T>(); | |
| 145 | + | return def; | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | template<typename T> | |
| 149 | + | T OTMLNode::valueAtIndex(int childIndex, const T& def) { | |
| 150 | + | if(OTMLNodePtr node = getIndex(childIndex)) | |
| 151 | + | return node->value<T>(); | |
| 152 | + | return def; | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | template<typename T> | |
| 156 | + | void OTMLNode::write(const T& v) { | |
| 157 | + | m_value = stdext::safe_cast<std::string>(v); | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | template<typename T> | |
| 161 | + | void OTMLNode::writeAt(const std::string& childTag, const T& v) { | |
| 162 | + | OTMLNodePtr child = OTMLNode::create(childTag); | |
| 163 | + | child->setUnique(true); | |
| 164 | + | child->write<T>(v); | |
| 165 | + | addChild(child); | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | template<typename T> | |
| 169 | + | void OTMLNode::writeIn(const T& v) { | |
| 170 | + | OTMLNodePtr child = OTMLNode::create(); | |
| 171 | + | child->write<T>(v); | |
| 172 | + | addChild(child); | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | #endif | |
| 176 | + |
| @@ -0,0 +1,205 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "otmlparser.h" | |
| 24 | + | #include "otmldocument.h" | |
| 25 | + | #include "otmlexception.h" | |
| 26 | + | #include <boost/tokenizer.hpp> | |
| 27 | + | ||
| 28 | + | OTMLParser::OTMLParser(OTMLDocumentPtr doc, std::istream& in) : | |
| 29 | + | currentDepth(0), currentLine(0), | |
| 30 | + | doc(doc), currentParent(doc), previousNode(0), | |
| 31 | + | in(in) | |
| 32 | + | { | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | void OTMLParser::parse() | |
| 36 | + | { | |
| 37 | + | if(!in.good()) | |
| 38 | + | throw OTMLException(doc, "cannot read from input stream"); | |
| 39 | + | ||
| 40 | + | while(!in.eof()) | |
| 41 | + | parseLine(getNextLine()); | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | std::string OTMLParser::getNextLine() | |
| 45 | + | { | |
| 46 | + | currentLine++; | |
| 47 | + | std::string line; | |
| 48 | + | std::getline(in, line); | |
| 49 | + | return line; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | int OTMLParser::getLineDepth(const std::string& line, bool multilining) | |
| 53 | + | { | |
| 54 | + | // count number of spaces at the line beginning | |
| 55 | + | std::size_t spaces = 0; | |
| 56 | + | while(line[spaces] == ' ') | |
| 57 | + | spaces++; | |
| 58 | + | ||
| 59 | + | // pre calculate depth | |
| 60 | + | int depth = spaces / 2; | |
| 61 | + | ||
| 62 | + | if(!multilining || depth <= currentDepth) { | |
| 63 | + | // check the next character is a tab | |
| 64 | + | if(line[spaces] == '\t') | |
| 65 | + | throw OTMLException(doc, "indentation with tabs are not allowed", currentLine); | |
| 66 | + | ||
| 67 | + | // must indent every 2 spaces | |
| 68 | + | if(spaces % 2 != 0) | |
| 69 | + | throw OTMLException(doc, "must indent every 2 spaces", currentLine); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | return depth; | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | void OTMLParser::parseLine(std::string line) | |
| 76 | + | { | |
| 77 | + | int depth = getLineDepth(line); | |
| 78 | + | ||
| 79 | + | if(depth == -1) | |
| 80 | + | return; | |
| 81 | + | ||
| 82 | + | // remove line sides spaces | |
| 83 | + | stdext::trim(line); | |
| 84 | + | ||
| 85 | + | // skip empty lines | |
| 86 | + | if(line.empty()) | |
| 87 | + | return; | |
| 88 | + | ||
| 89 | + | // skip comments | |
| 90 | + | if(stdext::starts_with(line, "//")) | |
| 91 | + | return; | |
| 92 | + | ||
| 93 | + | // a depth above, change current parent to the previous added node | |
| 94 | + | if(depth == currentDepth+1) { | |
| 95 | + | currentParent = previousNode; | |
| 96 | + | // a depth below, change parent to previous parent | |
| 97 | + | } else if(depth < currentDepth) { | |
| 98 | + | for(int i=0;i<currentDepth-depth;++i) | |
| 99 | + | currentParent = parentMap[currentParent]; | |
| 100 | + | // if it isn't the current depth, it's a syntax error | |
| 101 | + | } else if(depth != currentDepth) | |
| 102 | + | throw OTMLException(doc, "invalid indentation depth, are you indenting correctly?", currentLine); | |
| 103 | + | ||
| 104 | + | // sets current depth | |
| 105 | + | currentDepth = depth; | |
| 106 | + | ||
| 107 | + | // alright, new depth is set, the line is not empty and it isn't a comment | |
| 108 | + | // then it must be a node, so we parse it | |
| 109 | + | parseNode(line); | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | void OTMLParser::parseNode(const std::string& data) | |
| 113 | + | { | |
| 114 | + | std::string tag; | |
| 115 | + | std::string value; | |
| 116 | + | std::size_t dotsPos = data.find_first_of(':'); | |
| 117 | + | int nodeLine = currentLine; | |
| 118 | + | ||
| 119 | + | // node that has no tag and may have a value | |
| 120 | + | if(!data.empty() && data[0] == '-') { | |
| 121 | + | value = data.substr(1); | |
| 122 | + | stdext::trim(value); | |
| 123 | + | // node that has tag and possible a value | |
| 124 | + | } else if(dotsPos != std::string::npos) { | |
| 125 | + | tag = data.substr(0, dotsPos); | |
| 126 | + | if(data.size() > dotsPos+1) | |
| 127 | + | value = data.substr(dotsPos+1); | |
| 128 | + | // node that has only a tag | |
| 129 | + | } else { | |
| 130 | + | tag = data; | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | stdext::trim(tag); | |
| 134 | + | stdext::trim(value); | |
| 135 | + | ||
| 136 | + | // process multitine values | |
| 137 | + | if(value == "|" || value == "|-" || value == "|+") { | |
| 138 | + | // reads next lines until we can a value below the same depth | |
| 139 | + | std::string multiLineData; | |
| 140 | + | do { | |
| 141 | + | size_t lastPos = in.tellg(); | |
| 142 | + | std::string line = getNextLine(); | |
| 143 | + | int depth = getLineDepth(line, true); | |
| 144 | + | ||
| 145 | + | // depth above current depth, add the text to the multiline | |
| 146 | + | if(depth > currentDepth) { | |
| 147 | + | multiLineData += line.substr((currentDepth+1)*2); | |
| 148 | + | // it has contents below the current depth | |
| 149 | + | } else { | |
| 150 | + | // if not empty, its a node | |
| 151 | + | stdext::trim(line); | |
| 152 | + | if(!line.empty()) { | |
| 153 | + | // rewind and break | |
| 154 | + | in.seekg(lastPos, std::ios::beg); | |
| 155 | + | currentLine--; | |
| 156 | + | break; | |
| 157 | + | } | |
| 158 | + | } | |
| 159 | + | multiLineData += "\n"; | |
| 160 | + | } while(!in.eof()); | |
| 161 | + | ||
| 162 | + | /* determine how to treat new lines at the end | |
| 163 | + | * | strip all new lines at the end and add just a new one | |
| 164 | + | * |- strip all new lines at the end | |
| 165 | + | * |+ keep all the new lines at the end (the new lines until next node) | |
| 166 | + | */ | |
| 167 | + | if(value == "|" || value == "|-") { | |
| 168 | + | // remove all new lines at the end | |
| 169 | + | int lastPos = multiLineData.length(); | |
| 170 | + | while(multiLineData[--lastPos] == '\n') | |
| 171 | + | multiLineData.erase(lastPos, 1); | |
| 172 | + | ||
| 173 | + | if(value == "|") | |
| 174 | + | multiLineData.append("\n"); | |
| 175 | + | } // else it's |+ | |
| 176 | + | ||
| 177 | + | value = multiLineData; | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | // create the node | |
| 181 | + | OTMLNodePtr node = OTMLNode::create(tag); | |
| 182 | + | ||
| 183 | + | node->setUnique(dotsPos != std::string::npos); | |
| 184 | + | node->setTag(tag); | |
| 185 | + | node->setSource(doc->source() + ":" + stdext::unsafe_cast<std::string>(nodeLine)); | |
| 186 | + | ||
| 187 | + | // ~ is considered the null value | |
| 188 | + | if(value == "~") | |
| 189 | + | node->setNull(true); | |
| 190 | + | else { | |
| 191 | + | if(stdext::starts_with(value, "[") && stdext::ends_with(value, "]")) { | |
| 192 | + | std::string tmp = value.substr(1, value.length()-2); | |
| 193 | + | boost::tokenizer<boost::escaped_list_separator<char>> tokens(tmp); | |
| 194 | + | for(std::string v : tokens) { | |
| 195 | + | stdext::trim(v); | |
| 196 | + | node->writeIn(v); | |
| 197 | + | } | |
| 198 | + | } else | |
| 199 | + | node->setValue(value); | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | currentParent->addChild(node); | |
| 203 | + | parentMap[node] = currentParent; | |
| 204 | + | previousNode = node; | |
| 205 | + | } |
| @@ -0,0 +1,56 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef OTMLPARSER_H | |
| 24 | + | #define OTMLPARSER_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | ||
| 28 | + | class OTMLParser | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | OTMLParser(OTMLDocumentPtr doc, std::istream& in); | |
| 32 | + | ||
| 33 | + | /// Parse the entire document | |
| 34 | + | void parse(); | |
| 35 | + | ||
| 36 | + | private: | |
| 37 | + | /// Retrieve next line from the input stream | |
| 38 | + | std::string getNextLine(); | |
| 39 | + | /// Counts depth of a line (every 2 spaces increments one depth) | |
| 40 | + | int getLineDepth(const std::string& line, bool multilining = false); | |
| 41 | + | ||
| 42 | + | /// Parse each line of the input stream | |
| 43 | + | void parseLine(std::string line); | |
| 44 | + | /// Parse nodes tag and value | |
| 45 | + | void parseNode(const std::string& data); | |
| 46 | + | ||
| 47 | + | int currentDepth; | |
| 48 | + | int currentLine; | |
| 49 | + | OTMLDocumentPtr doc; | |
| 50 | + | OTMLNodePtr currentParent; | |
| 51 | + | std::unordered_map<OTMLNodePtr, OTMLNodePtr> parentMap; | |
| 52 | + | OTMLNodePtr previousNode; | |
| 53 | + | std::istream& in; | |
| 54 | + | }; | |
| 55 | + | ||
| 56 | + | #endif |
| @@ -0,0 +1,93 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef PCH_H | |
| 24 | + | #define PCH_H | |
| 25 | + | ||
| 26 | + | // common C headers | |
| 27 | + | #include <cstdio> | |
| 28 | + | #include <cstdlib> | |
| 29 | + | #include <cstddef> | |
| 30 | + | #include <cstring> | |
| 31 | + | #include <cmath> | |
| 32 | + | ||
| 33 | + | // common STL headers | |
| 34 | + | #include <iostream> | |
| 35 | + | #include <sstream> | |
| 36 | + | #include <string> | |
| 37 | + | #include <vector> | |
| 38 | + | #include <set> | |
| 39 | + | #include <list> | |
| 40 | + | #include <deque> | |
| 41 | + | #include <map> | |
| 42 | + | #include <algorithm> | |
| 43 | + | #include <functional> | |
| 44 | + | #include <array> | |
| 45 | + | #include <unordered_map> | |
| 46 | + | #include <unordered_set> | |
| 47 | + | #include <tuple> | |
| 48 | + | #include <iomanip> | |
| 49 | + | #include <typeinfo> | |
| 50 | + | ||
| 51 | + | // new | |
| 52 | + | #include <thread> | |
| 53 | + | #include <memory> | |
| 54 | + | #include <functional> | |
| 55 | + | #include <condition_variable> | |
| 56 | + | #include <mutex> | |
| 57 | + | #include <future> | |
| 58 | + | #include <chrono> | |
| 59 | + | #include <random> | |
| 60 | + | #ifndef ANDROID | |
| 61 | + | #include <filesystem> | |
| 62 | + | #endif | |
| 63 | + | ||
| 64 | + | // boost | |
| 65 | + | #ifdef ANDROID | |
| 66 | + | #define BOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX | |
| 67 | + | #endif | |
| 68 | + | #include <boost/system/config.hpp> | |
| 69 | + | #include <boost/system/error_code.hpp> | |
| 70 | + | #include <boost/asio.hpp> | |
| 71 | + | #include <boost/beast.hpp> | |
| 72 | + | ||
| 73 | + | #include <boost/asio/io_service.hpp> | |
| 74 | + | #include <boost/asio/ip/tcp.hpp> | |
| 75 | + | #ifndef __EMSCRIPTEN__ | |
| 76 | + | #include <boost/asio/ssl.hpp> | |
| 77 | + | ||
| 78 | + | #include <boost/beast/ssl.hpp> | |
| 79 | + | #endif | |
| 80 | + | #include <boost/beast/core/flat_buffer.hpp> | |
| 81 | + | #include <boost/beast/http/write.hpp> | |
| 82 | + | #include <boost/beast/http/string_body.hpp> | |
| 83 | + | #include <boost/beast/http/dynamic_body.hpp> | |
| 84 | + | #include <boost/beast/http/read.hpp> | |
| 85 | + | #include <boost/beast/http/parser.hpp> | |
| 86 | + | #ifndef __EMSCRIPTEN__ | |
| 87 | + | #include <boost/beast/websocket.hpp> | |
| 88 | + | #include <boost/beast/websocket/ssl.hpp> | |
| 89 | + | #endif | |
| 90 | + | ||
| 91 | + | #include <boost/algorithm/hex.hpp> | |
| 92 | + | ||
| 93 | + | #endif |
| @@ -0,0 +1,159 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2016 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifdef ANDROID | |
| 24 | + | ||
| 25 | + | #include <framework/global.h> | |
| 26 | + | ||
| 27 | + | #include "platform.h" | |
| 28 | + | #include "androidwindow.h" | |
| 29 | + | #include <fstream> | |
| 30 | + | #include <unistd.h> | |
| 31 | + | #include <string.h> | |
| 32 | + | #include <framework/stdext/stdext.h> | |
| 33 | + | #include <framework/core/eventdispatcher.h> | |
| 34 | + | ||
| 35 | + | #include <sys/stat.h> | |
| 36 | + | ||
| 37 | + | void Platform::processArgs(std::vector<std::string>& args) | |
| 38 | + | { | |
| 39 | + | //nothing todo, linux args are already utf8 encoded | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | bool Platform::spawnProcess(std::string process, const std::vector<std::string>& args) | |
| 43 | + | { | |
| 44 | + | return false; | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | int Platform::getProcessId() | |
| 48 | + | { | |
| 49 | + | return getpid(); | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | bool Platform::isProcessRunning(const std::string& name) | |
| 53 | + | { | |
| 54 | + | return false; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | bool Platform::killProcess(const std::string& name) | |
| 58 | + | { | |
| 59 | + | return false; | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | std::string Platform::getTempPath() | |
| 63 | + | { | |
| 64 | + | return "/tmp/"; | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | std::string Platform::getCurrentDir() | |
| 68 | + | { | |
| 69 | + | std::string res; | |
| 70 | + | char cwd[2048]; | |
| 71 | + | if (getcwd(cwd, sizeof(cwd)) != NULL) { | |
| 72 | + | res = cwd; | |
| 73 | + | res += "/"; | |
| 74 | + | } | |
| 75 | + | return res; | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | bool Platform::copyFile(std::string from, std::string to) | |
| 79 | + | { | |
| 80 | + | return true; | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | bool Platform::fileExists(std::string file) | |
| 84 | + | { | |
| 85 | + | return true; | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | bool Platform::removeFile(std::string file) | |
| 89 | + | { | |
| 90 | + | return false; | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | ticks_t Platform::getFileModificationTime(std::string file) | |
| 94 | + | { | |
| 95 | + | return 0; | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | bool Platform::openUrl(std::string url, bool now) | |
| 99 | + | { | |
| 100 | + | g_graphicsDispatcher.addEvent(std::bind(&AndroidWindow::openUrl, g_androidWindow, url)); | |
| 101 | + | return true; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | bool Platform::openDir(std::string path, bool now) | |
| 105 | + | { | |
| 106 | + | return true; | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | std::string Platform::getCPUName() | |
| 110 | + | { | |
| 111 | + | return std::string(); | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | double Platform::getTotalSystemMemory() | |
| 115 | + | { | |
| 116 | + | return 0; | |
| 117 | + | } | |
| 118 | + | ||
| 119 | + | double Platform::getMemoryUsage() | |
| 120 | + | { | |
| 121 | + | return 0; | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | std::string Platform::getOSName() | |
| 125 | + | { | |
| 126 | + | return "android"; | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | std::string Platform::traceback(const std::string& where, int level, int maxDepth) | |
| 130 | + | { | |
| 131 | + | return ""; | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | std::vector<std::string> Platform::getMacAddresses() | |
| 135 | + | { | |
| 136 | + | return std::vector<std::string>(); | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | std::string Platform::getUserName() | |
| 140 | + | { | |
| 141 | + | return "android"; | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | std::vector<std::string> Platform::getDlls() | |
| 145 | + | { | |
| 146 | + | return std::vector<std::string>(); | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | std::vector<std::string> Platform::getProcesses() | |
| 150 | + | { | |
| 151 | + | return std::vector<std::string>(); | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | std::vector<std::string> Platform::getWindows() | |
| 155 | + | { | |
| 156 | + | return std::vector<std::string>(); | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | #endif |
| @@ -0,0 +1,615 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2014 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include <framework/core/eventdispatcher.h> | |
| 24 | + | #include "androidwindow.h" | |
| 25 | + | ||
| 26 | + | AndroidWindow g_androidWindow; | |
| 27 | + | ||
| 28 | + | AndroidWindow::AndroidWindow() | |
| 29 | + | { | |
| 30 | + | m_minimumSize = Size(640, 360); | |
| 31 | + | m_size = Size(640, 360); | |
| 32 | + | m_eglDisplay = EGL_NO_DISPLAY; | |
| 33 | + | m_eglContext = EGL_NO_CONTEXT; | |
| 34 | + | m_eglSurface = EGL_NO_SURFACE; | |
| 35 | + | ||
| 36 | + | m_keyMap[AKEYCODE_BACK] = Fw::KeyEscape; | |
| 37 | + | m_keyMap[AKEYCODE_VOLUME_UP] = Fw::KeyF1; | |
| 38 | + | m_keyMap[AKEYCODE_VOLUME_DOWN] = Fw::KeyF2; | |
| 39 | + | m_keyMap[AKEYCODE_CAMERA] = Fw::KeyF3; | |
| 40 | + | ||
| 41 | + | m_keyMap[AKEYCODE_ESCAPE] = Fw::KeyEscape; | |
| 42 | + | m_keyMap[AKEYCODE_TAB] = Fw::KeyTab; | |
| 43 | + | m_keyMap[AKEYCODE_ENTER] = Fw::KeyEnter; | |
| 44 | + | m_keyMap[AKEYCODE_DEL] = Fw::KeyBackspace; | |
| 45 | + | m_keyMap[AKEYCODE_SPACE] = Fw::KeySpace; | |
| 46 | + | ||
| 47 | + | m_keyMap[AKEYCODE_PAGE_UP] = Fw::KeyPageUp; | |
| 48 | + | m_keyMap[AKEYCODE_PAGE_DOWN] = Fw::KeyPageDown; | |
| 49 | + | m_keyMap[AKEYCODE_MOVE_HOME] = Fw::KeyHome; | |
| 50 | + | m_keyMap[AKEYCODE_MOVE_END] = Fw::KeyEnd; | |
| 51 | + | m_keyMap[AKEYCODE_INSERT] = Fw::KeyInsert; | |
| 52 | + | m_keyMap[AKEYCODE_FORWARD_DEL] = Fw::KeyDelete; | |
| 53 | + | ||
| 54 | + | m_keyMap[AKEYCODE_DPAD_UP] = Fw::KeyUp; | |
| 55 | + | m_keyMap[AKEYCODE_DPAD_DOWN] = Fw::KeyDown; | |
| 56 | + | m_keyMap[AKEYCODE_DPAD_LEFT] = Fw::KeyLeft; | |
| 57 | + | m_keyMap[AKEYCODE_DPAD_RIGHT] = Fw::KeyRight; | |
| 58 | + | ||
| 59 | + | m_keyMap[AKEYCODE_NUM_LOCK] = Fw::KeyNumLock; | |
| 60 | + | m_keyMap[AKEYCODE_SCROLL_LOCK] = Fw::KeyScrollLock; | |
| 61 | + | m_keyMap[AKEYCODE_CAPS_LOCK] = Fw::KeyCapsLock; | |
| 62 | + | ||
| 63 | + | m_keyMap[AKEYCODE_CTRL_LEFT] = Fw::KeyCtrl; | |
| 64 | + | m_keyMap[AKEYCODE_CTRL_RIGHT] = Fw::KeyCtrl; | |
| 65 | + | m_keyMap[AKEYCODE_SHIFT_LEFT] = Fw::KeyShift; | |
| 66 | + | m_keyMap[AKEYCODE_SHIFT_RIGHT] = Fw::KeyShift; | |
| 67 | + | m_keyMap[AKEYCODE_ALT_LEFT] = Fw::KeyAlt; | |
| 68 | + | m_keyMap[AKEYCODE_ALT_RIGHT] = Fw::KeyAlt; | |
| 69 | + | ||
| 70 | + | m_keyMap[AKEYCODE_0] = Fw::Key0; | |
| 71 | + | m_keyMap[AKEYCODE_1] = Fw::Key1; | |
| 72 | + | m_keyMap[AKEYCODE_2] = Fw::Key2; | |
| 73 | + | m_keyMap[AKEYCODE_3] = Fw::Key3; | |
| 74 | + | m_keyMap[AKEYCODE_4] = Fw::Key4; | |
| 75 | + | m_keyMap[AKEYCODE_5] = Fw::Key5; | |
| 76 | + | m_keyMap[AKEYCODE_6] = Fw::Key6; | |
| 77 | + | m_keyMap[AKEYCODE_7] = Fw::Key7; | |
| 78 | + | m_keyMap[AKEYCODE_8] = Fw::Key8; | |
| 79 | + | m_keyMap[AKEYCODE_9] = Fw::Key9; | |
| 80 | + | ||
| 81 | + | m_keyMap[AKEYCODE_A] = Fw::KeyA; | |
| 82 | + | m_keyMap[AKEYCODE_B] = Fw::KeyB; | |
| 83 | + | m_keyMap[AKEYCODE_C] = Fw::KeyC; | |
| 84 | + | m_keyMap[AKEYCODE_D] = Fw::KeyD; | |
| 85 | + | m_keyMap[AKEYCODE_E] = Fw::KeyE; | |
| 86 | + | m_keyMap[AKEYCODE_F] = Fw::KeyF; | |
| 87 | + | m_keyMap[AKEYCODE_G] = Fw::KeyG; | |
| 88 | + | m_keyMap[AKEYCODE_H] = Fw::KeyH; | |
| 89 | + | m_keyMap[AKEYCODE_I] = Fw::KeyI; | |
| 90 | + | m_keyMap[AKEYCODE_J] = Fw::KeyJ; | |
| 91 | + | m_keyMap[AKEYCODE_K] = Fw::KeyK; | |
| 92 | + | m_keyMap[AKEYCODE_L] = Fw::KeyL; | |
| 93 | + | m_keyMap[AKEYCODE_M] = Fw::KeyM; | |
| 94 | + | m_keyMap[AKEYCODE_N] = Fw::KeyN; | |
| 95 | + | m_keyMap[AKEYCODE_O] = Fw::KeyO; | |
| 96 | + | m_keyMap[AKEYCODE_P] = Fw::KeyP; | |
| 97 | + | m_keyMap[AKEYCODE_Q] = Fw::KeyQ; | |
| 98 | + | m_keyMap[AKEYCODE_R] = Fw::KeyR; | |
| 99 | + | m_keyMap[AKEYCODE_S] = Fw::KeyS; | |
| 100 | + | m_keyMap[AKEYCODE_T] = Fw::KeyT; | |
| 101 | + | m_keyMap[AKEYCODE_U] = Fw::KeyU; | |
| 102 | + | m_keyMap[AKEYCODE_V] = Fw::KeyV; | |
| 103 | + | m_keyMap[AKEYCODE_W] = Fw::KeyW; | |
| 104 | + | m_keyMap[AKEYCODE_X] = Fw::KeyX; | |
| 105 | + | m_keyMap[AKEYCODE_Y] = Fw::KeyY; | |
| 106 | + | m_keyMap[AKEYCODE_Z] = Fw::KeyZ; | |
| 107 | + | ||
| 108 | + | m_keyMap[AKEYCODE_SEMICOLON] = Fw::KeySemicolon; | |
| 109 | + | m_keyMap[AKEYCODE_SLASH] = Fw::KeySlash; | |
| 110 | + | m_keyMap[AKEYCODE_GRAVE] = Fw::KeyGrave; | |
| 111 | + | m_keyMap[AKEYCODE_LEFT_BRACKET] = Fw::KeyLeftBracket; | |
| 112 | + | m_keyMap[AKEYCODE_BACKSLASH] = Fw::KeyBackslash; | |
| 113 | + | m_keyMap[AKEYCODE_RIGHT_BRACKET] = Fw::KeyRightBracket; | |
| 114 | + | m_keyMap[AKEYCODE_APOSTROPHE] = Fw::KeyApostrophe; | |
| 115 | + | m_keyMap[AKEYCODE_MINUS] = Fw::KeyMinus; | |
| 116 | + | m_keyMap[AKEYCODE_EQUALS] = Fw::KeyEqual; | |
| 117 | + | m_keyMap[AKEYCODE_COMMA] = Fw::KeyComma; | |
| 118 | + | m_keyMap[AKEYCODE_PERIOD] = Fw::KeyPeriod; | |
| 119 | + | ||
| 120 | + | m_keyMap[AKEYCODE_F1] = Fw::KeyF1; | |
| 121 | + | m_keyMap[AKEYCODE_F2] = Fw::KeyF2; | |
| 122 | + | m_keyMap[AKEYCODE_F3] = Fw::KeyF3; | |
| 123 | + | m_keyMap[AKEYCODE_F4] = Fw::KeyF4; | |
| 124 | + | m_keyMap[AKEYCODE_F5] = Fw::KeyF5; | |
| 125 | + | m_keyMap[AKEYCODE_F6] = Fw::KeyF6; | |
| 126 | + | m_keyMap[AKEYCODE_F7] = Fw::KeyF7; | |
| 127 | + | m_keyMap[AKEYCODE_F8] = Fw::KeyF8; | |
| 128 | + | m_keyMap[AKEYCODE_F9] = Fw::KeyF9; | |
| 129 | + | m_keyMap[AKEYCODE_F10] = Fw::KeyF10; | |
| 130 | + | m_keyMap[AKEYCODE_F11] = Fw::KeyF11; | |
| 131 | + | m_keyMap[AKEYCODE_F12] = Fw::KeyF12; | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | AndroidWindow::~AndroidWindow() | |
| 135 | + | { | |
| 136 | + | internalDestroyGL(); | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | ||
| 140 | + | void AndroidWindow::internalInitGL() | |
| 141 | + | { | |
| 142 | + | const EGLint attribs[] = { | |
| 143 | + | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |
| 144 | + | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |
| 145 | + | EGL_BLUE_SIZE, 8, | |
| 146 | + | EGL_GREEN_SIZE, 8, | |
| 147 | + | EGL_RED_SIZE, 8, | |
| 148 | + | EGL_ALPHA_SIZE, 8, | |
| 149 | + | EGL_NONE | |
| 150 | + | }; | |
| 151 | + | const EGLint context_attrib_list[] = { | |
| 152 | + | // request a context using Open GL ES 2.0 | |
| 153 | + | EGL_CONTEXT_CLIENT_VERSION, 2, | |
| 154 | + | EGL_NONE | |
| 155 | + | }; | |
| 156 | + | EGLint format; | |
| 157 | + | EGLint numConfigs; | |
| 158 | + | ||
| 159 | + | bool initDisplay = m_eglDisplay == EGL_NO_DISPLAY; | |
| 160 | + | if (initDisplay) | |
| 161 | + | m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| 162 | + | if (m_eglDisplay == EGL_NO_DISPLAY) | |
| 163 | + | g_logger.fatal("EGL not supported"); | |
| 164 | + | ||
| 165 | + | if (initDisplay) { | |
| 166 | + | if (!eglInitialize(m_eglDisplay, 0, 0)) | |
| 167 | + | g_logger.fatal("Unable to initialize EGL"); | |
| 168 | + | ||
| 169 | + | eglChooseConfig(m_eglDisplay, attribs, &m_eglConfig, 1, &numConfigs); | |
| 170 | + | eglGetConfigAttrib(m_eglDisplay, m_eglConfig, EGL_NATIVE_VISUAL_ID, &format); | |
| 171 | + | ANativeWindow_setBuffersGeometry(g_androidState->window, 0, 0, format); | |
| 172 | + | } | |
| 173 | + | ||
| 174 | + | m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, g_androidState->window, NULL); | |
| 175 | + | if (m_eglContext == EGL_NO_CONTEXT) | |
| 176 | + | m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, NULL, context_attrib_list); | |
| 177 | + | if (m_eglContext == EGL_NO_CONTEXT) | |
| 178 | + | g_logger.fatal(stdext::format("Unable to create EGL context: %i", eglGetError())); | |
| 179 | + | if (eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext) == EGL_FALSE) | |
| 180 | + | g_logger.fatal(stdext::format("Unable to eglMakeCurrent: %i", eglGetError())); | |
| 181 | + | ||
| 182 | + | updateSize(); | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | void AndroidWindow::internalDestroyGL() | |
| 186 | + | { | |
| 187 | + | if (m_eglDisplay != EGL_NO_DISPLAY) { | |
| 188 | + | eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | |
| 189 | + | //if (m_eglContext != EGL_NO_CONTEXT) { | |
| 190 | + | // eglDestroyContext(m_eglDisplay, m_eglContext); | |
| 191 | + | //} | |
| 192 | + | if (m_eglSurface != EGL_NO_SURFACE) { | |
| 193 | + | eglDestroySurface(m_eglDisplay, m_eglSurface); | |
| 194 | + | } | |
| 195 | + | //eglTerminate(m_eglDisplay); | |
| 196 | + | } | |
| 197 | + | //m_eglDisplay = EGL_NO_DISPLAY; | |
| 198 | + | //m_eglContext = EGL_NO_CONTEXT; | |
| 199 | + | m_eglSurface = EGL_NO_SURFACE; | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | void AndroidWindow::internalCheckGL() | |
| 203 | + | { | |
| 204 | + | m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| 205 | + | if (m_eglDisplay == EGL_NO_DISPLAY) | |
| 206 | + | g_logger.fatal("EGL not supported"); | |
| 207 | + | ||
| 208 | + | if (!eglInitialize(m_eglDisplay, NULL, NULL)) | |
| 209 | + | g_logger.fatal("Unable to initialize EGL"); | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | void AndroidWindow::internalChooseGL() | |
| 213 | + | { | |
| 214 | + | ||
| 215 | + | } | |
| 216 | + | ||
| 217 | + | void AndroidWindow::internalCreateGLContext() | |
| 218 | + | { | |
| 219 | + | EGLint attrList[] = { | |
| 220 | + | #if OPENGL_ES==2 | |
| 221 | + | EGL_CONTEXT_CLIENT_VERSION, 2, | |
| 222 | + | #else | |
| 223 | + | EGL_CONTEXT_CLIENT_VERSION, 1, | |
| 224 | + | #endif | |
| 225 | + | EGL_NONE | |
| 226 | + | }; | |
| 227 | + | ||
| 228 | + | m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, attrList); | |
| 229 | + | if (m_eglContext == EGL_NO_CONTEXT) | |
| 230 | + | g_logger.fatal(stdext::format("Unable to create EGL context: %s", eglGetError())); | |
| 231 | + | ||
| 232 | + | if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) | |
| 233 | + | g_logger.fatal("Unable to connect EGL context into Android native window"); | |
| 234 | + | } | |
| 235 | + | ||
| 236 | + | void AndroidWindow::internalDestroyGLContext() | |
| 237 | + | { | |
| 238 | + | /* | |
| 239 | + | if (m_window == NULL) | |
| 240 | + | return; | |
| 241 | + | ||
| 242 | + | if(m_eglDisplay) { | |
| 243 | + | eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | |
| 244 | + | ||
| 245 | + | if (m_eglSurface) { | |
| 246 | + | eglDestroySurface(m_eglDisplay, m_eglSurface); | |
| 247 | + | m_eglSurface = EGL_NO_SURFACE; | |
| 248 | + | } | |
| 249 | + | ||
| 250 | + | if(m_eglContext) { | |
| 251 | + | eglDestroyContext(m_eglDisplay, m_eglContext); | |
| 252 | + | m_eglContext = EGL_NO_CONTEXT; | |
| 253 | + | } | |
| 254 | + | ||
| 255 | + | eglTerminate(m_eglDisplay); | |
| 256 | + | m_eglDisplay = EGL_NO_DISPLAY; | |
| 257 | + | } */ | |
| 258 | + | } | |
| 259 | + | ||
| 260 | + | void AndroidWindow::internalConnectGLContext() | |
| 261 | + | { | |
| 262 | + | /* | |
| 263 | + | m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_window, NULL); | |
| 264 | + | if(m_eglSurface == EGL_NO_SURFACE) | |
| 265 | + | g_logger.fatal(stdext::format("Unable to create EGL surface: %s", eglGetError())); */ | |
| 266 | + | } | |
| 267 | + | ||
| 268 | + | void AndroidWindow::terminate() | |
| 269 | + | { | |
| 270 | + | //nativePause(); | |
| 271 | + | } | |
| 272 | + | ||
| 273 | + | void AndroidWindow::poll() | |
| 274 | + | { | |
| 275 | + | int ident; | |
| 276 | + | int events; | |
| 277 | + | struct android_poll_source* source; | |
| 278 | + | ||
| 279 | + | // If not animating, we will block forever waiting for events. | |
| 280 | + | // If animating, we loop until all events are read, then continue | |
| 281 | + | // to draw the next frame of animation. | |
| 282 | + | while ((ident = ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) { | |
| 283 | + | // Process this event. | |
| 284 | + | if (source != NULL) { | |
| 285 | + | source->process(g_androidState, source); | |
| 286 | + | } | |
| 287 | + | } | |
| 288 | + | ||
| 289 | + | g_dispatcher.addEvent(std::bind(&AndroidWindow::fireKeysPress, this)); | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | void AndroidWindow::swapBuffers() | |
| 293 | + | { | |
| 294 | + | eglSwapBuffers(m_eglDisplay, m_eglSurface); | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | void AndroidWindow::setVerticalSync(bool enable) | |
| 298 | + | { | |
| 299 | + | //eglSwapInterval(m_eglDisplay, enable ? 1 : 0); | |
| 300 | + | } | |
| 301 | + | ||
| 302 | + | std::string AndroidWindow::getClipboardText() | |
| 303 | + | { | |
| 304 | + | // TODO | |
| 305 | + | return ""; | |
| 306 | + | } | |
| 307 | + | ||
| 308 | + | void AndroidWindow::setClipboardText(const std::string& text) | |
| 309 | + | { | |
| 310 | + | // TODO | |
| 311 | + | } | |
| 312 | + | ||
| 313 | + | Size AndroidWindow::getDisplaySize() | |
| 314 | + | { | |
| 315 | + | return m_size; | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | std::string AndroidWindow::getPlatformType() | |
| 319 | + | { | |
| 320 | + | return "ANDROID-EGL"; | |
| 321 | + | } | |
| 322 | + | ||
| 323 | + | void AndroidWindow::init() {} | |
| 324 | + | ||
| 325 | + | void AndroidWindow::init(struct android_app* app) | |
| 326 | + | { | |
| 327 | + | g_androidState = app; | |
| 328 | + | g_androidState->userData = (void*)this; | |
| 329 | + | g_androidState->onAppCmd = static_cast<void(*)(android_app*, int32_t)>(+[](android_app* app, int32_t cmd) -> void { | |
| 330 | + | AndroidWindow* window = (AndroidWindow*)app->userData; | |
| 331 | + | if (window) | |
| 332 | + | return window->handleCmd(cmd); | |
| 333 | + | }); | |
| 334 | + | g_androidState->onInputEvent = static_cast<int32_t(*)(android_app*, AInputEvent*)>(+[](android_app* app, AInputEvent* event) -> int32_t { | |
| 335 | + | AndroidWindow* window = (AndroidWindow*)app->userData; | |
| 336 | + | if (window) | |
| 337 | + | return window->handleInput(event); | |
| 338 | + | return 0; | |
| 339 | + | }); | |
| 340 | + | } | |
| 341 | + | ||
| 342 | + | void AndroidWindow::show() {} | |
| 343 | + | ||
| 344 | + | void AndroidWindow::hide() {} | |
| 345 | + | ||
| 346 | + | void AndroidWindow::minimize() {} | |
| 347 | + | ||
| 348 | + | void AndroidWindow::maximize() {} | |
| 349 | + | ||
| 350 | + | void AndroidWindow::move(const Point& pos) {} | |
| 351 | + | ||
| 352 | + | void AndroidWindow::resize(const Size& size) {} | |
| 353 | + | ||
| 354 | + | void AndroidWindow::showMouse() {} | |
| 355 | + | ||
| 356 | + | void AndroidWindow::hideMouse() {} | |
| 357 | + | ||
| 358 | + | void AndroidWindow::setMouseCursor(int cursorId) {} | |
| 359 | + | ||
| 360 | + | void AndroidWindow::restoreMouseCursor() {} | |
| 361 | + | ||
| 362 | + | void AndroidWindow::setTitle(const std::string& title) {} | |
| 363 | + | ||
| 364 | + | void AndroidWindow::setMinimumSize(const Size& minimumSize) {} | |
| 365 | + | ||
| 366 | + | void AndroidWindow::setFullscreen(bool fullscreen) {} | |
| 367 | + | ||
| 368 | + | void AndroidWindow::setIcon(const std::string& iconFile) {} | |
| 369 | + | ||
| 370 | + | ||
| 371 | + | void AndroidWindow::handleCmd(int32_t cmd) | |
| 372 | + | { | |
| 373 | + | switch (cmd) { | |
| 374 | + | case APP_CMD_SAVE_STATE: | |
| 375 | + | break; | |
| 376 | + | case APP_CMD_INIT_WINDOW: | |
| 377 | + | if (g_androidState->window != NULL) { | |
| 378 | + | internalInitGL(); | |
| 379 | + | } | |
| 380 | + | releaseAllKeys(); | |
| 381 | + | break; | |
| 382 | + | case APP_CMD_TERM_WINDOW: | |
| 383 | + | m_visible = false; | |
| 384 | + | internalDestroyGL(); | |
| 385 | + | releaseAllKeys(); | |
| 386 | + | break; | |
| 387 | + | case APP_CMD_GAINED_FOCUS: | |
| 388 | + | m_visible = (m_eglContext != EGL_NO_CONTEXT); | |
| 389 | + | releaseAllKeys(); | |
| 390 | + | break; | |
| 391 | + | case APP_CMD_LOST_FOCUS: | |
| 392 | + | //m_visible = false; | |
| 393 | + | releaseAllKeys(); | |
| 394 | + | break; | |
| 395 | + | case APP_CMD_PAUSE: | |
| 396 | + | case APP_CMD_STOP: | |
| 397 | + | m_visible = false; | |
| 398 | + | releaseAllKeys(); | |
| 399 | + | break; | |
| 400 | + | case APP_CMD_DESTROY: | |
| 401 | + | if (m_onClose) | |
| 402 | + | m_onClose(); | |
| 403 | + | break; | |
| 404 | + | } | |
| 405 | + | updateSize(); | |
| 406 | + | } | |
| 407 | + | ||
| 408 | + | int AndroidWindow::handleInput(AInputEvent* event) | |
| 409 | + | { | |
| 410 | + | int32_t eventType = AInputEvent_getType(event); | |
| 411 | + | int32_t action = AKeyEvent_getAction(event); | |
| 412 | + | int32_t key_val = eventType == AINPUT_EVENT_TYPE_KEY ? AKeyEvent_getKeyCode(event) : 0; | |
| 413 | + | int pointerId = 0; | |
| 414 | + | Point mousePos; | |
| 415 | + | if (eventType == AINPUT_EVENT_TYPE_MOTION) { | |
| 416 | + | pointerId = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; | |
| 417 | + | mousePos = Point(AMotionEvent_getX(event, pointerId) / m_scaling, AMotionEvent_getY(event, pointerId) / m_scaling); | |
| 418 | + | } | |
| 419 | + | g_dispatcher.addEventEx("AndroidWindow::handleInput", [&, eventType, action, key_val, pointerId, mousePos] { | |
| 420 | + | if (!m_onInputEvent) return; | |
| 421 | + | static ticks_t lastPress = 0; | |
| 422 | + | static bool pressed = false; | |
| 423 | + | static Point touchStartPos(0, 0); | |
| 424 | + | if (eventType == AINPUT_EVENT_TYPE_MOTION) { | |
| 425 | + | int actionType = action & AMOTION_EVENT_ACTION_MASK; | |
| 426 | + | if (pointerId == 1 || pointerId == 2) { // multitouch | |
| 427 | + | if (actionType == AMOTION_EVENT_ACTION_POINTER_DOWN) { | |
| 428 | + | m_multiInputEvent[pointerId].reset(Fw::MousePressInputEvent); | |
| 429 | + | m_multiInputEvent[pointerId].mousePos = mousePos; | |
| 430 | + | m_multiInputEvent[pointerId].mouseButton = pointerId == 1 ? Fw::MouseTouch2 : Fw::MouseTouch3; | |
| 431 | + | m_mouseButtonStates[m_multiInputEvent[pointerId].mouseButton] = true; | |
| 432 | + | m_onInputEvent(m_multiInputEvent[pointerId]); | |
| 433 | + | } else if (actionType == AMOTION_EVENT_ACTION_POINTER_UP) { | |
| 434 | + | m_multiInputEvent[pointerId].reset(Fw::MouseReleaseInputEvent); | |
| 435 | + | m_multiInputEvent[pointerId].mousePos = mousePos; | |
| 436 | + | m_multiInputEvent[pointerId].mouseButton = pointerId == 1 ? Fw::MouseTouch2 : Fw::MouseTouch3; | |
| 437 | + | m_mouseButtonStates[m_multiInputEvent[pointerId].mouseButton] = false; | |
| 438 | + | m_onInputEvent(m_multiInputEvent[pointerId]); | |
| 439 | + | } | |
| 440 | + | return; | |
| 441 | + | } | |
| 442 | + | ||
| 443 | + | m_inputEvent.reset(Fw::MouseMoveInputEvent); | |
| 444 | + | m_inputEvent.mouseMoved = mousePos - m_inputEvent.mousePos; | |
| 445 | + | m_inputEvent.mousePos = mousePos; | |
| 446 | + | m_onInputEvent(m_inputEvent); | |
| 447 | + | ||
| 448 | + | if (actionType == AMOTION_EVENT_ACTION_DOWN) { | |
| 449 | + | lastPress = g_clock.millis(); | |
| 450 | + | m_inputEvent.reset(Fw::MousePressInputEvent); | |
| 451 | + | m_inputEvent.mouseButton = Fw::MouseTouch; | |
| 452 | + | m_mouseButtonStates[Fw::MouseTouch] = true; | |
| 453 | + | m_onInputEvent(m_inputEvent); | |
| 454 | + | touchStartPos = mousePos; | |
| 455 | + | } else if (actionType == AMOTION_EVENT_ACTION_UP || actionType == AMOTION_EVENT_ACTION_POINTER_UP) { | |
| 456 | + | m_inputEvent.reset(Fw::MouseReleaseInputEvent); | |
| 457 | + | m_inputEvent.mouseButton = Fw::MouseTouch; | |
| 458 | + | m_mouseButtonStates[Fw::MouseTouch] = false; | |
| 459 | + | m_onInputEvent(m_inputEvent); | |
| 460 | + | if (pointerId == 0) { | |
| 461 | + | if (!pressed) { | |
| 462 | + | if (lastPress + 500 < stdext::millis()) { | |
| 463 | + | m_inputEvent.reset(Fw::MousePressInputEvent); | |
| 464 | + | m_inputEvent.mouseButton = Fw::MouseRightButton; | |
| 465 | + | m_onInputEvent(m_inputEvent); | |
| 466 | + | ||
| 467 | + | m_inputEvent.reset(Fw::MouseReleaseInputEvent); | |
| 468 | + | m_inputEvent.mouseButton = Fw::MouseRightButton; | |
| 469 | + | m_onInputEvent(m_inputEvent); | |
| 470 | + | } else { | |
| 471 | + | lastPress = stdext::millis(); | |
| 472 | + | m_inputEvent.reset(Fw::MousePressInputEvent); | |
| 473 | + | m_inputEvent.mouseButton = Fw::MouseLeftButton; | |
| 474 | + | m_onInputEvent(m_inputEvent); | |
| 475 | + | ||
| 476 | + | m_inputEvent.reset(Fw::MouseReleaseInputEvent); | |
| 477 | + | m_inputEvent.mouseButton = Fw::MouseLeftButton; | |
| 478 | + | m_onInputEvent(m_inputEvent); | |
| 479 | + | } | |
| 480 | + | } else { | |
| 481 | + | pressed = false; | |
| 482 | + | m_inputEvent.reset(Fw::MouseReleaseInputEvent); | |
| 483 | + | m_inputEvent.mouseButton = Fw::MouseLeftButton; | |
| 484 | + | m_mouseButtonStates[Fw::MouseLeftButton] = false; | |
| 485 | + | m_onInputEvent(m_inputEvent); | |
| 486 | + | } | |
| 487 | + | } | |
| 488 | + | } else if (actionType == AMOTION_EVENT_ACTION_MOVE) { | |
| 489 | + | int dist = std::max(std::abs(touchStartPos.x - mousePos.x), std::abs(touchStartPos.y - mousePos.y)); | |
| 490 | + | if (!pressed && dist > 8) { | |
| 491 | + | pressed = true; | |
| 492 | + | m_inputEvent.reset(Fw::MouseMoveInputEvent); | |
| 493 | + | m_inputEvent.mouseMoved = touchStartPos - m_inputEvent.mousePos; | |
| 494 | + | m_inputEvent.mousePos = touchStartPos; | |
| 495 | + | m_onInputEvent(m_inputEvent); | |
| 496 | + | ||
| 497 | + | m_inputEvent.reset(Fw::MousePressInputEvent); | |
| 498 | + | m_inputEvent.mouseButton = Fw::MouseLeftButton; | |
| 499 | + | m_mouseButtonStates[Fw::MouseLeftButton] = true; | |
| 500 | + | m_onInputEvent(m_inputEvent); | |
| 501 | + | } | |
| 502 | + | } | |
| 503 | + | } else if (eventType == AINPUT_EVENT_TYPE_KEY) { | |
| 504 | + | Fw::Key key = Fw::KeyUnknown; | |
| 505 | + | if (m_keyMap.find(key_val) != m_keyMap.end()) | |
| 506 | + | key = m_keyMap[key_val]; | |
| 507 | + | if (action == AKEY_EVENT_ACTION_DOWN) { | |
| 508 | + | processKeyDown(key); | |
| 509 | + | } else if (action == AKEY_EVENT_ACTION_UP) { | |
| 510 | + | processKeyUp(key); | |
| 511 | + | } | |
| 512 | + | } | |
| 513 | + | }); | |
| 514 | + | if (eventType == AINPUT_EVENT_TYPE_MOTION) { | |
| 515 | + | return 1; | |
| 516 | + | } else if (eventType == AINPUT_EVENT_TYPE_KEY) { | |
| 517 | + | Fw::Key key = Fw::KeyUnknown; | |
| 518 | + | if (m_keyMap.find(key_val) != m_keyMap.end()) | |
| 519 | + | key = m_keyMap[key_val]; | |
| 520 | + | return key != Fw::KeyUnknown ? 1 : 0; | |
| 521 | + | } | |
| 522 | + | return 0; | |
| 523 | + | } | |
| 524 | + | ||
| 525 | + | void AndroidWindow::updateSize() | |
| 526 | + | { | |
| 527 | + | if (std::this_thread::get_id() != g_graphicsThreadId) { | |
| 528 | + | g_graphicsDispatcher.addEvent(std::bind(&AndroidWindow::updateSize, this)); | |
| 529 | + | return; | |
| 530 | + | } | |
| 531 | + | if (m_eglDisplay == EGL_NO_DISPLAY || m_eglSurface == EGL_NO_SURFACE) return; | |
| 532 | + | EGLint w, h; | |
| 533 | + | eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &w); | |
| 534 | + | eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &h); | |
| 535 | + | Size new_size(w, h); | |
| 536 | + | if (new_size == m_size) return; | |
| 537 | + | m_size = new_size; | |
| 538 | + | if (m_onResize) | |
| 539 | + | m_onResize(new_size); | |
| 540 | + | } | |
| 541 | + | ||
| 542 | + | ||
| 543 | + | void AndroidWindow::handleTextInput(std::string text) | |
| 544 | + | { | |
| 545 | + | if (!m_onInputEvent) return; | |
| 546 | + | g_dispatcher.addEventEx("AndroidWindow::handleTextInput", [&, text] { | |
| 547 | + | if (!m_onInputEvent) return; | |
| 548 | + | m_inputEvent.reset(Fw::KeyTextInputEvent); | |
| 549 | + | m_inputEvent.keyText = text; | |
| 550 | + | m_onInputEvent(m_inputEvent); | |
| 551 | + | }); | |
| 552 | + | } | |
| 553 | + | ||
| 554 | + | void AndroidWindow::showTextEditor(const std::string& title, const std::string& description, const std::string& text, int flags) | |
| 555 | + | { | |
| 556 | + | g_graphicsDispatcher.addEvent([&, title, description, text, flags] { | |
| 557 | + | JNIEnv* env = getJNIEnv(); | |
| 558 | + | JavaVMAttachArgs javaVMAttachArgs; | |
| 559 | + | javaVMAttachArgs.version = JNI_VERSION_1_6; | |
| 560 | + | javaVMAttachArgs.name = "NativeThread"; | |
| 561 | + | javaVMAttachArgs.group = NULL; | |
| 562 | + | ||
| 563 | + | jint nResult = g_androidState->activity->vm->AttachCurrentThread(&env, &javaVMAttachArgs); | |
| 564 | + | if (nResult != JNI_ERR) { | |
| 565 | + | jmethodID MethodShowKeyboard = env->GetMethodID(env->GetObjectClass(g_androidState->activity->clazz), "showTextEdit", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); | |
| 566 | + | env->CallVoidMethod(g_androidState->activity->clazz, MethodShowKeyboard, env->NewStringUTF(title.c_str()), env->NewStringUTF(description.c_str()), env->NewStringUTF(text.c_str()), flags); | |
| 567 | + | g_androidState->activity->vm->DetachCurrentThread(); | |
| 568 | + | } | |
| 569 | + | }); | |
| 570 | + | } | |
| 571 | + | ||
| 572 | + | void AndroidWindow::displayFatalError(const std::string& message) | |
| 573 | + | { | |
| 574 | + | JNIEnv* env = getJNIEnv(); | |
| 575 | + | JavaVM* vm = getJavaVM(); | |
| 576 | + | jint nResult = vm->AttachCurrentThread(&env, NULL); | |
| 577 | + | if (nResult != JNI_ERR) { | |
| 578 | + | jclass clazz = env->GetObjectClass(getClazz()); | |
| 579 | + | jmethodID methodID = env->GetMethodID(clazz, "displayFatalError", "(Ljava/lang/String;)V"); | |
| 580 | + | jstring jmessage = env->NewStringUTF(message.c_str()); | |
| 581 | + | env->CallVoidMethod(getClazz(), methodID, jmessage); | |
| 582 | + | env->DeleteLocalRef(jmessage); | |
| 583 | + | vm->DetachCurrentThread(); | |
| 584 | + | } | |
| 585 | + | } | |
| 586 | + | ||
| 587 | + | void AndroidWindow::openUrl(std::string url) | |
| 588 | + | { | |
| 589 | + | JNIEnv* env = getJNIEnv(); | |
| 590 | + | JavaVM* vm = getJavaVM(); | |
| 591 | + | jint nResult = vm->AttachCurrentThread(&env, NULL); | |
| 592 | + | if (nResult != JNI_ERR) { | |
| 593 | + | jclass clazz = env->GetObjectClass(getClazz()); | |
| 594 | + | jmethodID methodID = env->GetMethodID(clazz, "openUrl", "(Ljava/lang/String;)V"); | |
| 595 | + | jstring jmessage = env->NewStringUTF(url.c_str()); | |
| 596 | + | env->CallVoidMethod(getClazz(), methodID, jmessage); | |
| 597 | + | env->DeleteLocalRef(jmessage); | |
| 598 | + | vm->DetachCurrentThread(); | |
| 599 | + | } | |
| 600 | + | } | |
| 601 | + | ||
| 602 | + | extern "C" | |
| 603 | + | { | |
| 604 | + | JNIEXPORT void JNICALL Java_com_otclientv8_OTClientV8_commitText( | |
| 605 | + | JNIEnv* env, jobject obj, jstring text) | |
| 606 | + | { | |
| 607 | + | const char* newChar = env->GetStringUTFChars(text, NULL); | |
| 608 | + | std::string newText = newChar; | |
| 609 | + | env->ReleaseStringUTFChars(text, newChar); | |
| 610 | + | g_graphicsDispatcher.addEvent([newText] { | |
| 611 | + | g_window.handleTextInput(newText); | |
| 612 | + | }); | |
| 613 | + | } | |
| 614 | + | } | |
| 615 | + |
| @@ -0,0 +1,100 @@ | |||
| 1 | + | #ifndef ANDROID_WINDOW_H | |
| 2 | + | #define ANDROID_WINDOW_H | |
| 3 | + | ||
| 4 | + | #include "platformwindow.h" | |
| 5 | + | ||
| 6 | + | class AndroidWindow : public PlatformWindow { | |
| 7 | + | enum EventType { | |
| 8 | + | TOUCH_DOWN, | |
| 9 | + | TOUCH_UP, | |
| 10 | + | TOUCH_MOTION, | |
| 11 | + | TOUCH_LONGPRESS, | |
| 12 | + | KEY_DOWN, | |
| 13 | + | KEY_UP, | |
| 14 | + | TEXTINPUT, | |
| 15 | + | EVENT_UNDEFINED | |
| 16 | + | }; | |
| 17 | + | ||
| 18 | + | enum NativeMessage { | |
| 19 | + | RECREATE_CONTEXT, | |
| 20 | + | APP_TERMINATE | |
| 21 | + | }; | |
| 22 | + | ||
| 23 | + | void internalInitGL(); | |
| 24 | + | void internalDestroyGL(); | |
| 25 | + | ||
| 26 | + | void internalCheckGL(); | |
| 27 | + | void internalChooseGL(); | |
| 28 | + | void internalCreateGLContext(); | |
| 29 | + | void internalDestroyGLContext(); | |
| 30 | + | void internalConnectGLContext(); | |
| 31 | + | ||
| 32 | + | ||
| 33 | + | public: | |
| 34 | + | AndroidWindow(); | |
| 35 | + | ~AndroidWindow(); | |
| 36 | + | ||
| 37 | + | void init(); | |
| 38 | + | void init(struct android_app* app); | |
| 39 | + | void terminate(); | |
| 40 | + | void move(const Point& pos); | |
| 41 | + | void resize(const Size& size); | |
| 42 | + | void show(); | |
| 43 | + | void hide(); | |
| 44 | + | void minimize(); | |
| 45 | + | void maximize(); | |
| 46 | + | void poll(); | |
| 47 | + | void swapBuffers(); | |
| 48 | + | void showMouse(); | |
| 49 | + | void hideMouse(); | |
| 50 | + | ||
| 51 | + | void setMouseCursor(int cursorId); | |
| 52 | + | void restoreMouseCursor(); | |
| 53 | + | ||
| 54 | + | void setTitle(const std::string& title); | |
| 55 | + | void setMinimumSize(const Size& minimumSize); | |
| 56 | + | void setFullscreen(bool fullscreen); | |
| 57 | + | void setVerticalSync(bool enable); | |
| 58 | + | void setIcon(const std::string& iconFile); | |
| 59 | + | void setClipboardText(const std::string& text); | |
| 60 | + | ||
| 61 | + | Size getDisplaySize(); | |
| 62 | + | std::string getClipboardText(); | |
| 63 | + | std::string getPlatformType(); | |
| 64 | + | ||
| 65 | + | void displayFatalError(const std::string& message) override; | |
| 66 | + | void showTextEditor(const std::string& title, const std::string& description, const std::string& text, int flags) override; | |
| 67 | + | ||
| 68 | + | void handleCmd(int32_t cmd); | |
| 69 | + | int handleInput(AInputEvent* event); | |
| 70 | + | void updateSize(); | |
| 71 | + | void handleTextInput(std::string text); | |
| 72 | + | void openUrl(std::string url); | |
| 73 | + | ||
| 74 | + | protected: | |
| 75 | + | int internalLoadMouseCursor(const ImagePtr& image, const Point& hotSpot) override { return -1; }; | |
| 76 | + | ||
| 77 | + | JNIEnv* getJNIEnv() | |
| 78 | + | { | |
| 79 | + | return g_androidState->activity->env; | |
| 80 | + | } | |
| 81 | + | JavaVM* getJavaVM() | |
| 82 | + | { | |
| 83 | + | return g_androidState->activity->vm; | |
| 84 | + | } | |
| 85 | + | jobject getClazz() | |
| 86 | + | { | |
| 87 | + | return g_androidState->activity->clazz; | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | private: | |
| 91 | + | EGLConfig m_eglConfig; | |
| 92 | + | EGLContext m_eglContext; | |
| 93 | + | EGLDisplay m_eglDisplay; | |
| 94 | + | EGLSurface m_eglSurface; | |
| 95 | + | InputEvent m_multiInputEvent[3]; | |
| 96 | + | }; | |
| 97 | + | ||
| 98 | + | extern AndroidWindow g_androidWindow;; | |
| 99 | + | ||
| 100 | + | #endif |
| @@ -0,0 +1,31 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef CRASHHANDLER_H | |
| 24 | + | #define CRASHHANDLER_H | |
| 25 | + | ||
| 26 | + | #ifdef CRASH_HANDLER | |
| 27 | + | void installCrashHandler(); | |
| 28 | + | void uninstallCrashHandler(); | |
| 29 | + | #endif | |
| 30 | + | ||
| 31 | + | #endif |
| @@ -0,0 +1,25 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "platform.h" | |
| 24 | + | ||
| 25 | + | Platform g_platform; |
| @@ -0,0 +1,60 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef PLATFORM_H | |
| 24 | + | #define PLATFORM_H | |
| 25 | + | ||
| 26 | + | #include <string> | |
| 27 | + | #include <vector> | |
| 28 | + | #include <framework/stdext/types.h> | |
| 29 | + | ||
| 30 | + | class Platform | |
| 31 | + | { | |
| 32 | + | public: | |
| 33 | + | void processArgs(std::vector<std::string>& args); | |
| 34 | + | bool spawnProcess(std::string process, const std::vector<std::string>& args); | |
| 35 | + | int getProcessId(); | |
| 36 | + | bool isProcessRunning(const std::string& name); | |
| 37 | + | bool killProcess(const std::string& name); | |
| 38 | + | std::string getTempPath(); | |
| 39 | + | std::string getCurrentDir(); | |
| 40 | + | bool copyFile(std::string from, std::string to); | |
| 41 | + | bool fileExists(std::string file); | |
| 42 | + | bool removeFile(std::string file); | |
| 43 | + | ticks_t getFileModificationTime(std::string file); | |
| 44 | + | bool openUrl(std::string url, bool now = false); | |
| 45 | + | bool openDir(std::string path, bool now = false); | |
| 46 | + | std::string getCPUName(); | |
| 47 | + | double getTotalSystemMemory(); | |
| 48 | + | double getMemoryUsage(); | |
| 49 | + | std::string getOSName(); | |
| 50 | + | std::string traceback(const std::string& where, int level = 1, int maxDepth = 32); | |
| 51 | + | std::vector<std::string> getMacAddresses(); | |
| 52 | + | std::string getUserName(); | |
| 53 | + | std::vector<std::string> getDlls(); | |
| 54 | + | std::vector<std::string> getProcesses(); | |
| 55 | + | std::vector<std::string> getWindows(); | |
| 56 | + | }; | |
| 57 | + | ||
| 58 | + | extern Platform g_platform; | |
| 59 | + | ||
| 60 | + | #endif |
| @@ -0,0 +1,216 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "platformwindow.h" | |
| 24 | + | ||
| 25 | + | #if defined(WIN32) | |
| 26 | + | #include "win32window.h" | |
| 27 | + | WIN32Window window; | |
| 28 | + | #elif defined(ANDROID) | |
| 29 | + | #include "androidwindow.h" | |
| 30 | + | #elif defined(__EMSCRIPTEN__) | |
| 31 | + | #include "sdlwindow.h" | |
| 32 | + | SDLWindow window; | |
| 33 | + | #else | |
| 34 | + | #include "x11window.h" | |
| 35 | + | #include <framework/core/clock.h> | |
| 36 | + | X11Window window; | |
| 37 | + | #endif | |
| 38 | + | ||
| 39 | + | #include <framework/core/clock.h> | |
| 40 | + | #include <framework/core/eventdispatcher.h> | |
| 41 | + | #include <framework/graphics/image.h> | |
| 42 | + | ||
| 43 | + | #ifdef ANDROID | |
| 44 | + | PlatformWindow& g_window = g_androidWindow; | |
| 45 | + | #else | |
| 46 | + | PlatformWindow& g_window = window; | |
| 47 | + | #endif | |
| 48 | + | ||
| 49 | + | int PlatformWindow::loadMouseCursor(const std::string& file, const Point& hotSpot) | |
| 50 | + | { | |
| 51 | + | ImagePtr image = Image::load(file); | |
| 52 | + | ||
| 53 | + | if(!image) { | |
| 54 | + | g_logger.traceError(stdext::format("unable to load cursor image file %s", file)); | |
| 55 | + | return -1; | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | if(image->getBpp() != 4) { | |
| 59 | + | g_logger.error("the cursor image must have 4 channels"); | |
| 60 | + | return -1; | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | if(image->getWidth() != 32 || image->getHeight() != 32) { | |
| 64 | + | g_logger.error("the cursor image must have 32x32 dimension"); | |
| 65 | + | return -1; | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | return internalLoadMouseCursor(image, hotSpot); | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | void PlatformWindow::updateUnmaximizedCoords() | |
| 72 | + | { | |
| 73 | + | if(!isMaximized() && !isFullscreen()) { | |
| 74 | + | m_unmaximizedPos = m_position; | |
| 75 | + | m_unmaximizedSize = m_size; | |
| 76 | + | } | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | void PlatformWindow::processKeyDown(Fw::Key keyCode) | |
| 80 | + | { | |
| 81 | + | if (std::this_thread::get_id() != g_dispatcherThreadId) { | |
| 82 | + | g_dispatcher.addEvent(std::bind(&PlatformWindow::processKeyDown, this, keyCode)); | |
| 83 | + | return; | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | if(keyCode == Fw::KeyUnknown) | |
| 87 | + | return; | |
| 88 | + | ||
| 89 | + | if(keyCode == Fw::KeyCtrl) { | |
| 90 | + | m_inputEvent.keyboardModifiers |= Fw::KeyboardCtrlModifier; | |
| 91 | + | return; | |
| 92 | + | } else if(keyCode == Fw::KeyAlt) { | |
| 93 | + | m_inputEvent.keyboardModifiers |= Fw::KeyboardAltModifier; | |
| 94 | + | return; | |
| 95 | + | } else if(keyCode == Fw::KeyShift) { | |
| 96 | + | m_inputEvent.keyboardModifiers |= Fw::KeyboardShiftModifier; | |
| 97 | + | return; | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | if(m_keysState[keyCode]) | |
| 101 | + | return; | |
| 102 | + | ||
| 103 | + | m_keysState[keyCode] = true; | |
| 104 | + | m_lastKeysPress[keyCode] = -1; | |
| 105 | + | ||
| 106 | + | m_inputEvent.reset(Fw::KeyDownInputEvent); | |
| 107 | + | m_inputEvent.type = Fw::KeyDownInputEvent; | |
| 108 | + | m_inputEvent.keyCode = keyCode; | |
| 109 | + | ||
| 110 | + | if(m_onInputEvent) { | |
| 111 | + | m_onInputEvent(m_inputEvent); | |
| 112 | + | ||
| 113 | + | m_inputEvent.reset(Fw::KeyPressInputEvent); | |
| 114 | + | m_inputEvent.keyCode = keyCode; | |
| 115 | + | m_lastKeysPress[keyCode] = g_clock.millis(); | |
| 116 | + | m_firstKeysPress[keyCode] = g_clock.millis(); | |
| 117 | + | m_onInputEvent(m_inputEvent); | |
| 118 | + | } | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | void PlatformWindow::processKeyUp(Fw::Key keyCode) | |
| 122 | + | { | |
| 123 | + | if (std::this_thread::get_id() != g_dispatcherThreadId) { | |
| 124 | + | g_dispatcher.addEvent(std::bind(&PlatformWindow::processKeyUp, this, keyCode)); | |
| 125 | + | return; | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | if(keyCode == Fw::KeyUnknown) | |
| 129 | + | return; | |
| 130 | + | ||
| 131 | + | if(keyCode == Fw::KeyCtrl) { | |
| 132 | + | m_inputEvent.keyboardModifiers &= ~Fw::KeyboardCtrlModifier; | |
| 133 | + | return; | |
| 134 | + | } else if(keyCode == Fw::KeyAlt) { | |
| 135 | + | m_inputEvent.keyboardModifiers &= ~Fw::KeyboardAltModifier; | |
| 136 | + | return; | |
| 137 | + | } else if(keyCode == Fw::KeyShift) { | |
| 138 | + | m_inputEvent.keyboardModifiers &= ~Fw::KeyboardShiftModifier; | |
| 139 | + | return; | |
| 140 | + | } else if(keyCode == Fw::KeyNumLock) { | |
| 141 | + | for(uchar k = Fw::KeyNumpad0; k <= Fw::KeyNumpad9; ++k) { | |
| 142 | + | if(m_keysState[(Fw::Key)k]) | |
| 143 | + | processKeyUp((Fw::Key)k); | |
| 144 | + | } | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | if(!m_keysState[keyCode]) | |
| 148 | + | return; | |
| 149 | + | ||
| 150 | + | m_keysState[keyCode] = false; | |
| 151 | + | ||
| 152 | + | if(m_onInputEvent) { | |
| 153 | + | m_inputEvent.reset(Fw::KeyUpInputEvent); | |
| 154 | + | m_inputEvent.keyCode = keyCode; | |
| 155 | + | m_onInputEvent(m_inputEvent); | |
| 156 | + | } | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | void PlatformWindow::releaseAllKeys() | |
| 160 | + | { | |
| 161 | + | if (std::this_thread::get_id() != g_dispatcherThreadId) { | |
| 162 | + | g_dispatcher.addEvent(std::bind(&PlatformWindow::releaseAllKeys, this)); | |
| 163 | + | return; | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | for(auto it : m_keysState) { | |
| 167 | + | Fw::Key keyCode = it.first; | |
| 168 | + | bool pressed = it.second; | |
| 169 | + | ||
| 170 | + | if(!pressed) | |
| 171 | + | continue; | |
| 172 | + | ||
| 173 | + | processKeyUp(keyCode); | |
| 174 | + | } | |
| 175 | + | ||
| 176 | + | m_inputEvent.keyboardModifiers = 0; | |
| 177 | + | ||
| 178 | + | for(int i=0;i<Fw::MouseButtonLast + 1;++i) | |
| 179 | + | m_mouseButtonStates[i] = false; | |
| 180 | + | } | |
| 181 | + | ||
| 182 | + | void PlatformWindow::fireKeysPress() | |
| 183 | + | { | |
| 184 | + | if (std::this_thread::get_id() != g_dispatcherThreadId) { | |
| 185 | + | g_dispatcher.addEvent(std::bind(&PlatformWindow::fireKeysPress, this)); | |
| 186 | + | return; | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | // avoid massive checks | |
| 190 | + | if(m_keyPressTimer.ticksElapsed() < 10) | |
| 191 | + | return; | |
| 192 | + | m_keyPressTimer.restart(); | |
| 193 | + | ||
| 194 | + | for(auto it : m_keysState) { | |
| 195 | + | Fw::Key keyCode = it.first; | |
| 196 | + | bool pressed = it.second; | |
| 197 | + | ||
| 198 | + | if(!pressed) | |
| 199 | + | continue; | |
| 200 | + | ||
| 201 | + | ticks_t lastPressTicks = m_lastKeysPress[keyCode]; | |
| 202 | + | ticks_t firstKeyPress = m_firstKeysPress[keyCode]; | |
| 203 | + | if(g_clock.millis() - lastPressTicks >= KEY_PRESS_REPEAT_INTERVAL) { | |
| 204 | + | if(m_onInputEvent) { | |
| 205 | + | m_inputEvent.reset(Fw::KeyPressInputEvent); | |
| 206 | + | m_inputEvent.keyCode = keyCode; | |
| 207 | + | m_inputEvent.autoRepeatTicks = g_clock.millis() - firstKeyPress; | |
| 208 | + | m_onInputEvent(m_inputEvent); | |
| 209 | + | } | |
| 210 | + | m_lastKeysPress[keyCode] = g_clock.millis(); | |
| 211 | + | } | |
| 212 | + | } | |
| 213 | + | } | |
| 214 | + | ||
| 215 | + | void PlatformWindow::flash() { } | |
| 216 | + |