#include "classic/classic_mark_client.h" #include #include #include #include using namespace mtnet::classic; static std::vector take_all(ClassicStream &stream) { std::vector out(stream.outgoing_pending()); if (!out.empty()) { assert(stream.take_outgoing(out.data(), out.size()) == out.size()); } return out; } static void put_u16(std::vector &p, size_t off, uint16_t v) { p[off] = static_cast(v); p[off + 1] = static_cast(v >> 8); } static void put_u32(std::vector &p, size_t off, uint32_t v) { p[off] = static_cast(v); p[off + 1] = static_cast(v >> 8); p[off + 2] = static_cast(v >> 16); p[off + 3] = static_cast(v >> 24); } static void test_mark_download() { ClassicMarkClient client(0x11223344u, 0x55667788u); Handshake handshake{}; handshake.header = HDR_HANDSHAKE; handshake.handshake = 7; handshake.time = 100; client.stream().feed(&handshake, sizeof(handshake)); std::vector login = take_all(client.stream()); assert(login.size() == sizeof(Handshake) + sizeof(CGMarkLogin)); assert(login[sizeof(Handshake)] == HDR_CG_MARK_LOGIN); uint32_t handle = 0; std::memcpy(&handle, login.data() + sizeof(Handshake) + 1, sizeof(handle)); assert(handle == 0x11223344u); uint32_t random_key = 0; std::memcpy(&random_key, login.data() + sizeof(Handshake) + 5, sizeof(random_key)); assert(random_key == 0x55667788u); Handshake resync = handshake; resync.time = 200; resync.delta = 3; client.stream().feed(&resync, sizeof(resync)); std::vector repeated_login = take_all(client.stream()); assert(repeated_login.size() == sizeof(Handshake) + sizeof(CGMarkLogin)); assert(repeated_login[0] == HDR_HANDSHAKE); assert(repeated_login[sizeof(Handshake)] == HDR_CG_MARK_LOGIN); std::vector phase{HDR_GC_PHASE, PHASE_LOGIN}; client.stream().feed(phase.data(), phase.size()); std::vector request = take_all(client.stream()); assert(request.size() == sizeof(CGMarkIDXList)); assert(request[0] == HDR_CG_MARK_IDXLIST); // GuildMarkDownloader.cpp:172/201 frames HEADER_GC_MARK_DIFF_DATA (101) as a // bare header byte and ignores it; the next mark frame must still parse. const uint8_t diff = 101; client.stream().feed(&diff, 1); assert(client.stream().last_error().empty()); // One guild maps to image zero, position one. Feed the whole-size frame in // two chunks to exercise the custom raw framing path. std::vector idx(sizeof(GCMarkIDXList) + 4); idx[0] = HDR_GC_MARK_IDXLIST; put_u32(idx, 1, static_cast(idx.size())); put_u16(idx, 5, 1); put_u16(idx, 7, 42); put_u16(idx, 9, 1); client.stream().feed(idx.data(), 6); assert(client.stream().outgoing_pending() == 0); client.stream().feed(idx.data() + 6, idx.size() - 6); request = take_all(client.stream()); assert(request.size() == sizeof(CGMarkCRCList)); assert(request[0] == HDR_CG_MARK_CRCLIST && request[1] == 0); assert(client.marks().has_mark(42)); // A valid zero-block image response advances the image state and completes // the download without requiring LZO data in this framing test. std::vector block(sizeof(GCMarkBlock)); block[0] = HDR_GC_MARK_BLOCK; put_u32(block, 1, static_cast(block.size())); block[5] = 0; put_u32(block, 6, 0); client.stream().feed(block.data(), block.size()); assert(client.complete()); } static void test_symbol_download() { ClassicMarkClient client(0x11u, 0x22u); client.set_download_symbol(42); std::vector phase{HDR_GC_PHASE, PHASE_LOGIN}; client.stream().feed(phase.data(), phase.size()); std::vector request = take_all(client.stream()); assert(request.size() == sizeof(CGSymbolCRC)); assert(request[0] == HDR_CG_SYMBOL_CRC); uint32_t gid = 0; std::memcpy(&gid, request.data() + 1, sizeof(gid)); assert(gid == 42); const std::vector payload{0xAA, 0xBB, 0xCC}; std::vector symbol(sizeof(GCSymbolData) + payload.size()); symbol[0] = HDR_GC_SYMBOL_DATA; put_u16(symbol, 1, static_cast(symbol.size())); put_u32(symbol, 3, 42); std::memcpy(symbol.data() + sizeof(GCSymbolData), payload.data(), payload.size()); client.stream().feed(symbol.data(), symbol.size()); assert(client.complete()); assert(client.symbol_guild_id() == 42); assert(client.symbol_data() == payload); } static void test_upload_layouts() { uint32_t px[GUILD_MARK_WIDTH * GUILD_MARK_HEIGHT] = {}; px[0] = 0x44332211u; ClassicMarkClient mark(1, 2); mark.set_upload_mark(42, px); std::vector phase{HDR_GC_PHASE, PHASE_LOGIN}; mark.stream().feed(phase.data(), phase.size()); std::vector packet = take_all(mark.stream()); assert(packet.size() == sizeof(CGMarkUpload)); assert(packet[0] == HDR_CG_MARK_UPLOAD); uint32_t gid = 0; std::memcpy(&gid, packet.data() + 1, sizeof(gid)); assert(gid == 42); assert(packet[5] == 0x11 && packet[6] == 0x22 && packet[7] == 0x33 && packet[8] == 0x44); ClassicMarkClient symbol(3, 4); symbol.set_upload_symbol(99, std::vector{1, 2, 3}); symbol.stream().feed(phase.data(), phase.size()); packet = take_all(symbol.stream()); assert(packet.size() == sizeof(CGGuildSymbolUpload) + 3); assert(packet[0] == HDR_CG_GUILD_SYMBOL_UPLOAD); assert(packet[1] == packet.size() && packet[2] == 0); std::memcpy(&gid, packet.data() + 3, sizeof(gid)); assert(gid == 99); assert(packet[7] == 1 && packet[8] == 2 && packet[9] == 3); } int main() { test_mark_download(); test_symbol_download(); test_upload_layouts(); return 0; }