1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2004-2009  The Mana World Development Team
4  *  Copyright (C) 2009-2010  The Mana Developers
5  *  Copyright (C) 2011-2019  The ManaPlus Developers
6  *  Copyright (C) 2019-2021  Andrei Karas
7  *
8  *  This file is part of The ManaPlus Client.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "net/messageout.h"
25 
26 #include "logger.h"
27 
28 #include "net/net.h"
29 #include "net/packetcounters.h"
30 
31 #include "utils/stringutils.h"
32 
33 PRAGMA48(GCC diagnostic push)
34 PRAGMA48(GCC diagnostic ignored "-Wshadow")
35 #include <SDL_endian.h>
36 PRAGMA48(GCC diagnostic pop)
37 
38 #include "debug.h"
39 
40 #ifndef SDL_BIG_ENDIAN
41 #error missing SDL_endian.h
42 #endif  // SDL_BYTEORDER
43 
44 extern int itemIdLen;
45 
46 namespace Net
47 {
48 
MessageOut(const int16_t id)49 MessageOut::MessageOut(const int16_t id) :
50     mData(nullptr),
51     mDataSize(0),
52     mPos(0),
53     mId(id),
54     mIgnore(false)
55 {
56     PacketCounters::incOutPackets();
57     IGNOREDEBUGLOG;
58     DEBUGLOG2("Send packet", 0, "MessageOut");
59 }
60 
writeInt8(const int8_t value,const char * const str)61 void MessageOut::writeInt8(const int8_t value, const char *const str)
62 {
63     expand(1);
64     mData[mPos] = value;
65     DEBUGLOG2("writeInt8:  " + toStringPrint(CAST_U32(
66         CAST_U8(value))),
67         mPos, str);
68     mPos += 1;
69 }
70 
writeInt16(const int16_t value,const char * const str)71 void MessageOut::writeInt16(const int16_t value, const char *const str)
72 {
73     expand(2);
74 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
75     int16_t swap = SDL_Swap16(value);
76     memcpy(mData + CAST_SIZE(mPos), &swap, sizeof(int16_t));
77 #else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
78 
79     memcpy(mData + CAST_SIZE(mPos), &value, sizeof(int16_t));
80 #endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
81 
82     DEBUGLOG2("writeInt16: " + toStringPrint(CAST_U32(
83         CAST_U16(value))),
84         mPos, str);
85     mPos += 2;
86 }
87 
writeInt32(const int32_t value,const char * const str)88 void MessageOut::writeInt32(const int32_t value, const char *const str)
89 {
90     DEBUGLOG2("writeInt32: " + toStringPrint(CAST_U32(value)),
91         mPos, str);
92     expand(4);
93 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
94     int32_t swap = SDL_Swap32(value);
95     memcpy(mData + CAST_SIZE(mPos), &swap, sizeof(int32_t));
96 #else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
97 
98     memcpy(mData + CAST_SIZE(mPos), &value, sizeof(int32_t));
99 #endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
100 
101     mPos += 4;
102 }
103 
writeItemId(const int32_t value,const char * const str)104 void MessageOut::writeItemId(const int32_t value,
105                              const char *const str)
106 {
107     if (itemIdLen == 2)
108         writeInt16(CAST_S16(value), str);
109     else
110         writeInt32(value, str);
111 }
112 
writeInt64(const int64_t value,const char * const str)113 void MessageOut::writeInt64(const int64_t value, const char *const str)
114 {
115     DEBUGLOG2("writeInt64: " + toStringPrint(CAST_U32(value)),
116         mPos, str);
117     expand(8);
118 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
119     int32_t swap = SDL_Swap64(value);
120     memcpy(mData + CAST_SIZE(mPos), &swap, sizeof(int64_t));
121 #else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
122 
123     memcpy(mData + CAST_SIZE(mPos), &value, sizeof(int64_t));
124 #endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
125 
126     mPos += 8;
127 }
128 
writeBeingId(const BeingId value,const char * const str)129 void MessageOut::writeBeingId(const BeingId value, const char *const str)
130 {
131     writeInt32(toInt(value, int32_t), str);
132 }
133 
writeFloat(const float value,const char * const str)134 void MessageOut::writeFloat(const float value, const char *const str)
135 {
136 #ifdef ENABLEDEBUGLOG
137     std::string text = strprintf("writeFloat: %f", value);
138     DEBUGLOG2(text, mPos, str);
139 #endif
140     expand(4);
141     memcpy(mData + CAST_SIZE(mPos), &value, sizeof(float));
142     mPos += 4;
143 }
144 
145 #define LOBYTE(w) (CAST_U8(w))
146 #define HIBYTE(w) (CAST_U8((CAST_U16(w)) >> 8))
147 
writeCoordinates(const uint16_t x,const uint16_t y,unsigned char direction,const char * const str)148 void MessageOut::writeCoordinates(const uint16_t x,
149                                   const uint16_t y,
150                                   unsigned char direction,
151                                   const char *const str)
152 {
153     DEBUGLOG2(strprintf("writeCoordinates: %u,%u %u",
154         CAST_U32(x),
155         CAST_U32(y),
156         CAST_U32(direction)), mPos, str);
157     unsigned char *const data = reinterpret_cast<unsigned char*>(mData)
158         + CAST_SIZE(mPos);
159     expand(3);
160     mPos += 3;
161 
162     uint16_t temp = x;
163     temp <<= 6;
164     data[0] = 0;
165     data[1] = 1;
166     data[2] = 2;
167     data[0] = HIBYTE(temp);
168     data[1] = CAST_U8(temp);
169     temp = y;
170     temp <<= 4;
171     data[1] |= HIBYTE(temp);
172     data[2] = LOBYTE(temp);
173     direction = toServerDirection(direction);
174     data[2] |= direction;
175 }
176 
writeString(const std::string & string,int length,const char * const str)177 void MessageOut::writeString(const std::string &string,
178                              int length,
179                              const char *const str)
180 {
181     int stringLength = CAST_S32(string.length());
182     if (length < 0)
183     {
184         // Write the length at the start if not fixed
185         writeInt16(CAST_S16(stringLength), "len");
186         length = stringLength;
187     }
188     else if (length < stringLength)
189     {
190         // Make sure the length of the string is no longer than specified
191         stringLength = length;
192     }
193     expand(length);
194 
195     // Write the actual string
196     memcpy(mData + CAST_SIZE(mPos), string.c_str(), stringLength);
197 
198     // Pad remaining space with zeros
199     if (length > stringLength)
200     {
201         memset(mData + CAST_SIZE(mPos + stringLength),
202             '\0',
203             length - stringLength);
204     }
205 
206     DEBUGLOG2("writeString: " + string, mPos, str);
207     mPos += length;
208 }
209 
writeStringNoLog(const std::string & string,int length,const char * const str)210 void MessageOut::writeStringNoLog(const std::string &string,
211                                   int length,
212                                   const char *const str)
213 {
214     int stringLength = CAST_S32(string.length());
215     if (length < 0)
216     {
217         // Write the length at the start if not fixed
218         writeInt16(CAST_S16(stringLength), "len");
219         length = stringLength;
220     }
221     else if (length < stringLength)
222     {
223         // Make sure the length of the string is no longer than specified
224         stringLength = length;
225     }
226     expand(length);
227 
228     // Write the actual string
229     memcpy(mData + CAST_SIZE(mPos), string.c_str(), stringLength);
230 
231     // Pad remaining space with zeros
232     if (length > stringLength)
233     {
234         memset(mData + CAST_SIZE(mPos + stringLength),
235             '\0',
236             length - stringLength);
237     }
238 
239     DEBUGLOG2("writeString: ***", mPos, str);
240     mPos += length;
241 }
242 
getData() const243 const char *MessageOut::getData() const
244 {
245     return mData;
246 }
247 
getDataSize() const248 unsigned int MessageOut::getDataSize() const
249 {
250     return mDataSize;
251 }
252 
toServerDirection(unsigned char direction)253 unsigned char MessageOut::toServerDirection(unsigned char direction)
254 {
255     // Translate direction to eAthena format
256     switch (direction)
257     {
258         case 1:  // DOWN
259             direction = 0;
260             break;
261         case 3:  // DOWN | LEFT
262             direction = 1;
263             break;
264         case 2:  // LEFT
265             direction = 2;
266             break;
267         case 6:  // LEFT | UP
268             direction = 3;
269             break;
270         case 4:  // UP
271             direction = 4;
272             break;
273         case 12:  // UP | RIGHT
274             direction = 5;
275             break;
276         case 8:  // RIGHT
277             direction = 6;
278             break;
279         case 9:  // RIGHT + DOWN
280             direction = 7;
281             break;
282         default:
283             // OOPSIE! Impossible or unknown
284             direction = CAST_U8(-1);
285             break;
286     }
287     return direction;
288 }
289 
290 }  // namespace Net
291