#!/usr/bin/env python # coding: utf-8 # protocol.py - generator for entire TMWA network protocol # # Copyright © 2014 Ben Longbons # # This file is part of The Mana World (Athena server) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import glob import itertools import os from posixpath import relpath # The following code should be relatively easy to understand, but please # keep your sanity fastened and your arms and legs inside at all times. # important note: all numbers in this file will make a lot more sense in # decimal, but they're written in hex. generated = '// This is a generated file, edit %s instead\n' % __file__ copyright = '''// {filename} - {description} // // Copyright © 2014 Ben Longbons // // This file is part of The Mana World (Athena server) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . ''' class LowType(object): __slots__ = () class NativeType(LowType): __slots__ = ('name') def __init__(self, name): self.name = name def a_tag(self): return self.name class NetworkType(LowType): __slots__ = ('name') def __init__(self, name): self.name = name def e_tag(self): return self.name class Type(object): __slots__ = () class NeutralType(Type): __slots__ = ('name') def __init__(self, name): self.name = name def native_tag(self): return self.name def network_tag(self): return self.name e_tag = network_tag class StringType(Type): __slots__ = ('native') def __init__(self, native): self.native = native def native_tag(self): return self.native.a_tag() def network_tag(self): return 'NetString' % self.native.a_tag() def dump(self, f): # not implemented properly, uses a template in the meta instead pass class ProvidedType(Type): __slots__ = ('native', 'network') def __init__(self, native, network): self.native = native self.network = network def native_tag(self): return self.native.a_tag() def network_tag(self): return self.network.e_tag() class EnumType(Type): __slots__ = ('native', 'under') def __init__(self, native, under): self.native = native self.under = under def native_tag(self): return self.native.a_tag() def network_tag(self): return self.under.network_tag() def dump(self, f): native = self.native_tag() under = self.under.native_tag() network = self.network_tag() f.write('inline __attribute__((warn_unused_result))\n') f.write('bool native_to_network({0} *network, {1} native)\n{{\n'.format(network, native)) f.write(' bool rv = true;\n') f.write(' {0} tmp = static_cast<{0}>(native);\n'.format(under)) f.write(' rv &= native_to_network(network, tmp);\n') f.write(' return rv;\n') f.write('}\n') f.write('inline __attribute__((warn_unused_result))\n') f.write('bool network_to_native({0} *native, {1} network)\n{{\n'.format(native, network)) f.write(' bool rv = true;\n') f.write(' {0} tmp;\n'.format(under)) f.write(' rv &= network_to_native(&tmp, network);\n') f.write(' *native = static_cast<{0}>(tmp);\n'.format(native)) f.write(' // TODO this is what really should be doing a checked cast\n') f.write(' return rv;\n') f.write('}\n') class WrappedType(Type): __slots__ = ('native', 'under') def __init__(self, native, under): self.native = native self.under = under def native_tag(self): return self.native.a_tag() def network_tag(self): return self.under.network_tag() def dump(self, f): native = self.native_tag() under = self.under.native_tag() network = self.network_tag() f.write('inline __attribute__((warn_unused_result))\n') f.write('bool native_to_network({0} *network, {1} native)\n{{\n'.format(network, native)) f.write(' bool rv = true;\n') f.write(' {0} tmp = unwrap<{1}>(native);\n'.format(under, native)) f.write(' rv &= native_to_network(network, tmp);\n') f.write(' return rv;\n') f.write('}\n') f.write('inline __attribute__((warn_unused_result))\n') f.write('bool network_to_native({0} *native, {1} network)\n{{\n'.format(native, network)) f.write(' bool rv = true;\n') f.write(' {0} tmp;\n'.format(under)) f.write(' rv &= network_to_native(&tmp, network);\n') f.write(' *native = wrap<{0}>(tmp);\n'.format(native)) f.write(' return rv;\n') f.write('}\n') class SkewLengthType(Type): __slots__ = ('type', 'skew') def __init__(self, ty, skew): self.type = ty self.skew = skew def native_tag(self): return self.type.native_tag() def network_tag(self): return 'SkewedLength<%s, %d>' % (self.type.network_tag(), self.skew) def dumpx(self): # not registered properly, so the method name is wrong # TODO remind myself to kill this code with fire before release # not implemented properly, uses a template in the meta instead # insert another comment here so that it lines up properly pass class StructType(Type): __slots__ = ('id', 'name', 'fields', 'size') def __init__(self, id, name, fields, size): self.id = id self.name = name self.fields = fields self.size = size def native_tag(self): return self.name def network_tag(self): return 'Net' + self.name def dump(self, f): self.dump_native(f) self.dump_network(f) self.dump_convert(f) f.write('\n') def dump_fwd(self, fwd): if self.id is not None: fwd.write('template<>\n') fwd.write('struct %s;\n' % self.name) if self.id is not None: fwd.write('template<>\n') fwd.write('struct Net%s;\n' % self.name) def dump_native(self, f): name = self.name if self.id is not None: f.write('template<>\n') f.write('struct %s\n{\n' % name) if self.id is not None: f.write(' static const uint16_t PACKET_ID = 0x%04x;\n\n' % self.id) for (o, l, n) in self.fields: if n in ('magic_packet_id', 'magic_packet_length'): f.write(' // TODO remove this\n') if n == 'magic_packet_id': f.write(' %s %s = PACKET_ID;\n' % (l.native_tag(), n)) else: f.write(' %s %s = {};\n' % (l.native_tag(), n)) f.write('};\n') def dump_network(self, f): name = 'Net%s' % self.name if self.id is not None: f.write('template<>\n') f.write('struct %s\n{\n' % name) for (o, l, n) in self.fields: f.write(' %s %s;\n' % (l.network_tag(), n)) f.write('};\n') for (o, l, n) in self.fields: if o is not None: s = 'offsetof(%s, %s) == %d' % (name, n, o) f.write('static_assert({0}, "{0}");\n'.format(s)) if self.size is not None: s = 'sizeof(%s) == %d' % (name, self.size) f.write('static_assert({0}, "{0}");\n'.format(s)) def dump_convert(self, f): f.write('inline __attribute__((warn_unused_result))\n') f.write('bool native_to_network(Net{0} *network, {0} native)\n{{\n'.format(self.name)) f.write(' bool rv = true;\n') for (o, l, n) in self.fields: f.write(' rv &= native_to_network(&network->{0}, native.{0});\n'.format(n)) f.write(' return rv;\n') f.write('}\n') f.write('inline __attribute__((warn_unused_result))\n') f.write('bool network_to_native({0} *native, Net{0} network)\n{{\n'.format(self.name)) f.write(' bool rv = true;\n') for (o, l, n) in self.fields: f.write(' rv &= network_to_native(&native->{0}, network.{0});\n'.format(n)) f.write(' return rv;\n') f.write('}\n') class PartialStructType(Type): __slots__ = ('native', 'body') def __init__(self, native, body): self.native = native self.body = body def native_tag(self): return self.native.a_tag() def network_tag(self): return 'Net%s' % self.native_tag() def dump(self, f): f.write('struct %s\n{\n' % self.network_tag()) for n, t in self.body: f.write(' %s %s;\n' % (t.network_tag(), n)) f.write('};\n') f.write('inline __attribute__((warn_unused_result))\n') f.write('bool native_to_network(Net{0} *network, {0} native)\n{{\n'.format(self.native_tag())) f.write(' bool rv = true;\n') for n, t in self.body: u = t.native_tag() # probably not necessary f.write(' {1} {0} = native.{0}; rv &= native_to_network(&network->{0}, {0});\n'.format(n, u)) f.write(' return rv;\n') f.write('}\n') f.write('inline __attribute__((warn_unused_result))\n') f.write('bool network_to_native({0} *native, Net{0} network)\n{{\n'.format(self.native_tag())) f.write(' bool rv = true;\n') for n, t in self.body: # the temporary is ABSOLUTELY NECESSARY here # the native type is permitted to differ from the declared type # e.g. HumanTimeDiff uses int16_t instead of uint16_t u = t.native_tag() f.write(' {1} {0}; rv &= network_to_native(&{0}, network.{0}); native->{0} = {0};\n'.format(n, u)) f.write(' return rv;\n') f.write('}\n') f.write('\n') class Include(object): __slots__ = ('path', '_types') def __init__(self, path): self.path = path self._types = [] def testcase(self, outdir): basename = os.path.basename(self.path.strip('<">')) root = os.path.splitext(basename)[0] filename = 'include_%s_test.cpp' % root desc = 'testsuite for protocol includes' poison = relpath('src/poison.hpp', outdir) with open(os.path.join(outdir, filename), 'w') as f: f.write(self.pp(0)) f.write(copyright.format(filename=filename, description=desc)) f.write('\n') f.write('#include "%s"\n\n' % poison) for t in self._types: f.write('using %s = %s;\n' % ('Test_' + ident(t.name), t.name)) def pp(self, n): return '#%*sinclude %s\n' % (n, '', self.path) def native(self, name): ty = NativeType(name) self._types.append(ty) return ty def network(self, name): ty = NetworkType(name) self._types.append(ty) return ty def neutral(self, name): ty = NeutralType(name) self._types.append(ty) return ty class FixedPacket(object): __slots__ = ('fixed_struct') def __init__(self, fixed_struct): self.fixed_struct = fixed_struct def dump_fwd(self, fwd): self.fixed_struct.dump_fwd(fwd) fwd.write('\n') def dump_native(self, f): self.fixed_struct.dump_native(f) f.write('\n') def dump_network(self, f): self.fixed_struct.dump_network(f) f.write('\n') def dump_convert(self, f): self.fixed_struct.dump_convert(f) f.write('\n') class VarPacket(object): __slots__ = ('head_struct', 'repeat_struct') def __init__(self, head_struct, repeat_struct): self.head_struct = head_struct self.repeat_struct = repeat_struct def dump_fwd(self, fwd): self.head_struct.dump_fwd(fwd) self.repeat_struct.dump_fwd(fwd) fwd.write('\n') def dump_native(self, f): self.head_struct.dump_native(f) self.repeat_struct.dump_native(f) f.write('\n') def dump_network(self, f): self.head_struct.dump_network(f) self.repeat_struct.dump_network(f) f.write('\n') def dump_convert(self, f): self.head_struct.dump_convert(f) self.repeat_struct.dump_convert(f) f.write('\n') def packet(id, fixed=None, fixed_size=None, payload=None, payload_size=None, head=None, head_size=None, repeat=None, repeat_size=None, option=None, option_size=None, ): assert (fixed is None) <= (fixed_size is None) assert (payload is None) <= (payload_size is None) assert (head is None) <= (head_size is None) assert (repeat is None) <= (repeat_size is None) assert (option is None) <= (option_size is None) if fixed is not None: assert not head and not repeat and not option and not payload return FixedPacket( StructType(id, 'Packet_Fixed<0x%04x>' % id, fixed, fixed_size)) elif payload is not None: assert not head and not repeat and not option return FixedPacket( StructType(id, 'Packet_Payload<0x%04x>' % id, payload, payload_size)) else: assert head if option: return VarPacket( StructType(id, 'Packet_Head<0x%04x>' % id, head, head_size), StructType(id, 'Packet_Option<0x%04x>' % id, option, option_size)) else: assert repeat return VarPacket( StructType(id, 'Packet_Head<0x%04x>' % id, head, head_size), StructType(id, 'Packet_Repeat<0x%04x>' % id, repeat, repeat_size)) class Channel(object): __slots__ = ('server', 'client', 'packets') def __init__(self, server, client): self.server = server self.client = client self.packets = [] def s(self, id, **kwargs): self.packets.append(packet(id, **kwargs)) r = s def dump(self, outdir, fwd): server = self.server client = self.client header = '%s-%s.hpp' % (server, client) test = '%s-%s_test.cpp' % (server, client) desc = 'TMWA network protocol: %s/%s' % (server, client) with open(os.path.join(outdir, header), 'w') as f: proto2 = relpath(outdir, 'src') define = ('TMWA_%s_%s_%s_HPP' % (proto2, server, client)).upper() f.write('#ifndef %s\n' % define) f.write('#define %s\n' % define) f.write(copyright.format(filename=header, description=desc)) f.write('\n') f.write(generated) f.write('\n') f.write('# include "fwd.hpp"\n\n') f.write('# include "types.hpp"\n') f.write('\n') if client == 'user': f.write('// This is a public protocol, and changes require client cooperation\n') else: f.write('// This is an internal protocol, and can be changed without notice\n') f.write('\n') f.write('// this is only needed for the payload packet right now, and that needs to die\n') f.write('#pragma pack(push, 1)\n') f.write('\n') for p in self.packets: p.dump_fwd(fwd) fwd.write('\n') for p in self.packets: p.dump_native(f) f.write('\n') for p in self.packets: p.dump_network(f) f.write('\n') for p in self.packets: p.dump_convert(f) f.write('\n') f.write('#pragma pack(pop)\n') f.write('\n') f.write('#endif // %s\n' % define) with open(os.path.join(outdir, test), 'w') as f: poison = relpath('src/poison.hpp', outdir) f.write('#include "%s"\n' % header) f.write(copyright.format(filename=test, description=desc)) f.write('\n') f.write(generated) f.write('\n') f.write('#include "{}"\n'.format(poison)) ident_translation = ''.join(chr(c) if chr(c).isalnum() else '_' for c in range(256)) def ident(s): if s == 'packet id': return 'magic_packet_id' if s == 'packet length': return 'magic_packet_length' return s.translate(ident_translation) def at(o, l, n): return (o, l, ident(n)) class Context(object): __slots__ = ('outdir', '_includes', '_channels', '_types') def __init__(self, outdir): self.outdir = outdir self._includes = [] self._channels = [] self._types = [] def sysinclude(self, name): rv = Include('<%s>' % name) self._includes.append(rv) return rv def include(self, name): rv = Include('"%s"' % relpath(name, self.outdir)) self._includes.append(rv) return rv def chan(self, server, client): ch = Channel(server, client) self._channels.append(ch) return ch def dump(self): outdir = self.outdir for g in glob.glob(os.path.join(outdir, '*.[ch]pp')): os.remove(g) proto2 = relpath(outdir, 'src') with open(os.path.join(outdir, 'fwd.hpp'), 'w') as f: header = '%s/fwd.hpp' % proto2 desc = 'Forward declarations of network packets' sanity = relpath('src/sanity.hpp', outdir) define = ('TMWA_%s_FWD_HPP' % proto2).upper() f.write('#ifndef %s\n' % define) f.write('#define %s\n' % define) f.write(copyright.format(filename=header, description=desc)) f.write('\n') f.write('# include "%s"\n\n' % sanity) f.write('# include \n\n') for b in ['Fixed', 'Payload', 'Head', 'Repeat', 'Option']: c = 'Packet_' + b f.write('template class %s;\n' % c) f.write('template class Net%s;\n' % c) f.write('\n') for ch in self._channels: ch.dump(outdir, f) f.write('\n') f.write('#endif // %s\n' % define) with open(os.path.join(outdir, 'types.hpp'), 'w') as f: header = '%s/types.hpp' % proto2 desc = 'Forward declarations of packet component types' define = ('TMWA_%s_TYPES_HPP' % proto2).upper() f.write('#ifndef %s\n' % define) f.write('#define %s\n' % define) f.write(copyright.format(filename=header, description=desc)) f.write('\n') f.write('# include "fwd.hpp"\n\n') f.write('//TODO split the includes\n') for inc in self._includes: f.write(inc.pp(1)) # this is writing another file inc.testcase(outdir) f.write('template\n') f.write('bool native_to_network(T *network, T native)\n{\n') f.write(' *network = native;\n') f.write(' return true;\n') f.write('}\n') f.write('template\n') f.write('bool network_to_native(T *native, T network)\n{\n') f.write(' *native = network;\n') f.write(' return true;\n') f.write('}\n') f.write('template\n') f.write('struct NetString\n{\n') f.write(' char data[N];\n') f.write('};\n') f.write('template\n') f.write('bool native_to_network(NetString *network, VString native)\n{\n') f.write(' // basically WBUF_STRING\n') f.write(' char *const begin = network->data;\n') f.write(' char *const end = begin + N;\n') f.write(' char *const mid = std::copy(native.begin(), native.end(), begin);\n') f.write(' std::fill(mid, end, \'\\0\');\n') f.write(' return true;\n') f.write('}\n') f.write('template\n') f.write('bool network_to_native(VString *native, NetString network)\n{\n') f.write(' // basically RBUF_STRING\n') f.write(' const char *const begin = network.data;\n') f.write(' const char *const end = begin + N;\n') f.write(' const char *const mid = std::find(begin, end, \'\\0\');\n') f.write(' *native = XString(begin, mid, nullptr);\n') f.write(' return true;\n') f.write('}\n') f.write('\n') f.write('inline\n') f.write('bool native_to_network(NetString<24> *network, CharName native)\n{\n') f.write(' VString<23> tmp = native.to__actual();\n') f.write(' bool rv = native_to_network(network, tmp);\n') f.write(' return rv;\n') f.write('}\n') f.write('inline\n') f.write('bool network_to_native(CharName *native, NetString<24> network)\n{\n') f.write(' VString<23> tmp;\n') f.write(' bool rv = network_to_native(&tmp, network);\n') f.write(' *native = stringish(tmp);\n') f.write(' return rv;\n') f.write('}\n') f.write('\n') f.write('inline\n') f.write('bool native_to_network(NetString<16> *network, MapName native)\n{\n') f.write(' XString tmp = native;\n') f.write(' bool rv = native_to_network(network, VString<15>(tmp));\n') f.write(' return rv;\n') f.write('}\n') f.write('inline\n') f.write('bool network_to_native(MapName *native, NetString<16> network)\n{\n') f.write(' VString<15> tmp;\n') f.write(' bool rv = network_to_native(&tmp, network);\n') f.write(' *native = stringish(tmp);\n') f.write(' return rv;\n') f.write('}\n') f.write('\n') f.write('template\n') f.write('struct SkewedLength\n{\n') f.write(' T data;\n') f.write('};\n') f.write('template\n') f.write('bool native_to_network(SkewedLength *network, U native)\n{\n') f.write(' native -= N;\n') f.write(' return native_to_network(&network->data, native);\n') f.write('}\n') f.write('template\n') f.write('bool network_to_native(U *native, SkewedLength network)\n{\n') f.write(' bool rv = network_to_native(native, network.data);\n') f.write(' *native += N;\n') f.write(' return rv;\n') f.write('}\n') f.write('\n') for ty in self._types: ty.dump(f) f.write('#endif // %s\n' % define) # types def provided(self, native, network): # the whole point of 'provided' is to not be implemented return ProvidedType(native, network) def enum(self, native, under): rv = EnumType(native, under) self._types.append(rv) return rv def wrap(self, native, under): rv = WrappedType(native, under) self._types.append(rv) return rv def string(self, native): rv = StringType(native) self._types.append(rv) return rv def struct(self, name, body, size): rv = StructType(None, name, body, size) self._types.append(rv) return rv def partial_struct(self, native, body): rv = PartialStructType(native, body) self._types.append(rv) return rv def main(): # setup ctx = Context(outdir='src/proto2/') # headers cstdint = ctx.sysinclude('cstdint') endians_h = ctx.include('src/ints/little.hpp') vstring_h = ctx.include('src/strings/vstring.hpp') ip_h = ctx.include('src/net/ip.hpp') enums_h = ctx.include('src/mmo/enums.hpp') human_time_diff_h = ctx.include('src/mmo/human_time_diff.hpp') ids_h = ctx.include('src/mmo/ids.hpp') mmo_h = ctx.include('src/mmo/mmo.hpp') strs_h = ctx.include('src/mmo/strs.hpp') utils_h = ctx.include('src/mmo/utils.hpp') version_h = ctx.include('src/mmo/version.hpp') login_types_h = ctx.include('src/login/types.hpp') # included types uint8_t = cstdint.native('uint8_t') uint16_t = cstdint.native('uint16_t') uint32_t = cstdint.native('uint32_t') uint64_t = cstdint.native('uint64_t') Byte = endians_h.neutral('Byte') Little16 = endians_h.network('Little16') Little32 = endians_h.network('Little32') Little64 = endians_h.network('Little64') SEX = enums_h.native('SEX') Option = enums_h.native('Option') Species = ids_h.native('Species') AccountId = ids_h.native('AccountId') CharId = ids_h.native('CharId') PartyId = ids_h.native('PartyId') ItemNameId = ids_h.native('ItemNameId') BlockId = ids_h.native('BlockId') GmLevel = ids_h.native('GmLevel') HumanTimeDiff = human_time_diff_h.native('HumanTimeDiff') Version = version_h.native('Version') ip4 = ip_h.neutral('IP4Address') TimeT = utils_h.native('TimeT') VString16 = vstring_h.native('VString<15>') VString20 = vstring_h.native('VString<19>') VString24 = vstring_h.native('VString<23>') VString32 = vstring_h.native('VString<31>') VString40 = vstring_h.native('VString<39>') # not all of these are used on the network side of things # should this set of numbers be +1'ed ? timestamp_seconds_buffer = utils_h.native('timestamp_seconds_buffer') timestamp_milliseconds_buffer = utils_h.native('timestamp_milliseconds_buffer') AccountName = strs_h.native('AccountName') AccountPass = strs_h.native('AccountPass') #AccountCrypt = strs_h.native('AccountCrypt') AccountEmail = strs_h.native('AccountEmail') ServerName = strs_h.native('ServerName') PartyName = strs_h.native('PartyName') VarName = strs_h.native('VarName') CharName = strs_h.native('CharName') MapName = strs_h.native('MapName') #MobName = map_t_h.native('MobName') #NpcName = map_t_h.native('NpcName') #ScriptLabel = map_t_h.native('ScriptLabel') #ItemName = map_t_h.native('ItemName') #md5_native = md5_h.native('md5_native') #SaltString = md5_h.native('SaltString') VERSION_2 = login_types_h.native('VERSION_2') # TODO: fix LIES char_key = mmo_h.neutral('CharKey') char_data = mmo_h.neutral('CharData') party_most = mmo_h.neutral('PartyMost') storage = mmo_h.neutral('Storage') # generated types u8 = ctx.provided(uint8_t, Byte) u16 = ctx.provided(uint16_t, Little16) u32 = ctx.provided(uint32_t, Little32) u64 = ctx.provided(uint64_t, Little64) sex_char = ctx.provided(SEX, NeutralType('char')) sex = ctx.enum(SEX, u8) option = ctx.enum(Option, u16) species = ctx.wrap(Species, u16) account_id = ctx.wrap(AccountId, u32) char_id = ctx.wrap(CharId, u32) party_id = ctx.wrap(PartyId, u32) item_name_id = ctx.wrap(ItemNameId, u16) item_name_id4 = ctx.wrap(ItemNameId, u32) block_id = ctx.wrap(BlockId, u32) time32 = ctx.provided(TimeT, Little32) time64 = ctx.provided(TimeT, Little64) gm1 = ctx.provided(GmLevel, Byte) gm2 = ctx.provided(GmLevel, Little16) gm = ctx.provided(GmLevel, Little32) str16 = ctx.string(VString16) str20 = ctx.string(VString20) str24 = ctx.string(VString24) str32 = ctx.string(VString32) str40 = ctx.string(VString40) seconds = ctx.string(timestamp_seconds_buffer) millis = ctx.string(timestamp_milliseconds_buffer) account_name = ctx.string(AccountName) account_pass = ctx.string(AccountPass) #account_crypt = ctx.string(AccountCrypt) account_email = ctx.string(AccountEmail) server_name = ctx.string(ServerName) party_name = ctx.string(PartyName) var_name = ctx.string(VarName) char_name = ctx.string(CharName) map_name = ctx.string(MapName) #mob_name = ctx.string(MobName) #npc_name = ctx.string(NpcName) #script_label = ctx.string(ScriptLabel) #item_name = ctx.string(ItemName) #md5_string = ctx.string(md5_string) #salt_string = ctx.string(SaltString) # this will be *so* useful when I do the party copy! human_time_diff = ctx.partial_struct( HumanTimeDiff, [ ('year', u16), ('month', u16), ('day', u16), ('hour', u16), ('minute', u16), ('second', u16), ] ) version = ctx.partial_struct( Version, [ ('major', u8), ('minor', u8), ('patch', u8), ('devel', u8), ('flags', u8), ('which', u8), ('vend', u16), ] ) version_2 = ctx.enum(VERSION_2, u8) stats6 = ctx.struct( 'Stats6', [ at(0, u8, 'str'), at(1, u8, 'agi'), at(2, u8, 'vit'), at(3, u8, 'int_'), at(4, u8, 'dex'), at(5, u8, 'luk'), ], size=6, ) char_select = ctx.struct( 'CharSelect', [ at(0, char_id, 'char id'), at(4, u32, 'base exp'), at(8, u32, 'zeny'), at(12, u32, 'job exp'), at(16, u32, 'job level'), at(20, item_name_id, 'shoes'), at(22, item_name_id, 'gloves'), at(24, item_name_id, 'cape'), at(26, item_name_id, 'misc1'), at(28, option, 'option'), at(30, u16, 'unused'), at(32, u32, 'karma'), at(36, u32, 'manner'), at(40, u16, 'status point'), at(42, u16, 'hp'), at(44, u16, 'max hp'), at(46, u16, 'sp'), at(48, u16, 'max sp'), at(50, u16, 'speed'), at(52, species, 'species'), at(54, u16, 'hair_style'), at(56, u16, 'weapon'), at(58, u16, 'base level'), at(60, u16, 'skill point'), at(62, item_name_id, 'head bottom'), at(64, item_name_id, 'shield'), at(66, item_name_id, 'head top'), at(68, item_name_id, 'head mid'), at(70, u16, 'hair color'), at(72, item_name_id, 'misc2'), at(74, char_name, 'char name'), at(98, stats6, 'stats'), at(104, u8, 'char num'), at(105, u8, 'unused2'), ], size=106, ) # packets # this is a somewhat simplistic view. For packets that get forwarded, # it may be worth pretending something like admin->char, map->login ... # that does break the tree format though login_char = ctx.chan('login', 'char') login_admin = ctx.chan('login', 'admin') login_bot = NotImplemented login_user = ctx.chan('login', 'user') char_map = ctx.chan('char', 'map') char_admin = NotImplemented char_bot = NotImplemented char_user = ctx.chan('char', 'user') map_admin = NotImplemented map_bot = NotImplemented map_user = ctx.chan('map', 'user') any_user = ctx.chan('any', 'user') # * user char_user.r(0x0061, fixed=[ at(0, u16, 'packet id'), at(2, account_pass, 'old pass'), at(26, account_pass, 'new pass'), ], fixed_size=50, ) char_user.s(0x0062, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'status'), ], fixed_size=3, ) login_user.r(0x0063, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[at(0, u8, 'c')], repeat_size=1, ) login_user.r(0x0064, fixed=[ at(0, u16, 'packet id'), at(2, u32, 'unknown'), at(6, account_name, 'account name'), at(30, account_pass, 'account pass'), at(54, version_2, 'version 2 flags'), ], fixed_size=55, ) char_user.r(0x0065, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u32, 'login id1'), at(10, u32, 'login id2'), at(14, u16, 'packet tmw version'), at(16, sex, 'sex'), ], fixed_size=17, ) char_user.r(0x0066, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, ) char_user.r(0x0067, fixed=[ at(0, u16, 'packet id'), at(2, char_name, 'char name'), at(26, stats6, 'stats'), at(32, u8, 'slot'), at(33, u16, 'hair color'), at(35, u16, 'hair style'), ], fixed_size=37, ) char_user.r(0x0068, fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), at(6, account_email, 'email'), ], fixed_size=46, ) login_user.r(0x0069, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, u32, 'login id1'), at(8, account_id, 'account id'), at(12, u32, 'login id2'), at(16, u32, 'unused'), at(20, millis, 'last login string'), at(44, u16, 'unused2'), at(46, sex, 'sex'), ], head_size=47, repeat=[ at(0, ip4, 'ip'), at(4, u16, 'port'), at(6, server_name, 'server name'), at(26, u16, 'users'), at(28, u16, 'maintenance'), at(30, u16, 'is new'), ], repeat_size=32, ) login_user.s(0x006a, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'error code'), at(3, seconds, 'error message'), ], fixed_size=23, ) char_user.s(0x006b, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, str20, 'unused'), ], head_size=24, repeat=[ at(0, char_select, 'char select'), ], repeat_size=106, ) char_user.s(0x006c, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, ) char_user.s(0x006d, fixed=[ at(0, u16, 'packet id'), at(2, char_select, 'char select'), ], fixed_size=108, ) char_user.s(0x006e, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, ) char_user.s(0x006f, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) char_user.s(0x0070, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, ) char_user.s(0x0071, fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), at(6, map_name, 'map name'), at(22, ip4, 'ip'), at(26, u16, 'port'), ], fixed_size=28, ) any_user.s(0x0081, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'error code'), ], fixed_size=3, ) map_user.s(0x0212, fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'npc id'), at(6, u16, 'command'), at(8, block_id, 'id'), at(12, u16, 'x'), at(14, u16, 'y'), ], fixed_size=16, ) # login char login_char.r(0x2709, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) login_char.r(0x2710, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_pass, 'account pass'), at(50, u32, 'unknown'), at(54, ip4, 'ip'), at(58, u16, 'port'), at(60, server_name, 'server name'), at(80, u16, 'unknown2'), at(82, u16, 'maintenance'), at(84, u16, 'is_new'), ], fixed_size=86, ) login_char.s(0x2711, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, ) login_char.r(0x2712, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u32, 'login id1'), at(10, u32, 'login id2'), at(14, sex, 'sex'), at(15, ip4, 'ip'), ], fixed_size=19, ) login_char.s(0x2713, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'invalid'), at(7, account_email, 'email'), at(47, time32, 'connect until'), ], fixed_size=51, ) login_char.r(0x2714, fixed=[ at(0, u16, 'packet id'), at(2, u32, 'users'), ], fixed_size=6, ) login_char.r(0x2715, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_email, 'email'), ], fixed_size=46, ) login_char.r(0x2716, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) login_char.s(0x2717, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_email, 'email'), at(46, time32, 'connect until'), ], fixed_size=50, ) # 0x2b0a login_char.r(0x2720, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), ], head_size=8, repeat=[at(0, u8, 'c')], repeat_size=1, ) login_char.s(0x2721, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, gm, 'gm level'), ], fixed_size=10, ) # 0x2b0c login_char.r(0x2722, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_email, 'old email'), at(46, account_email, 'new email'), ], fixed_size=86, ) login_char.s(0x2723, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, sex, 'sex'), ], fixed_size=7, ) login_char.r(0x2724, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u32, 'status'), ], fixed_size=10, ) login_char.r(0x2725, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, human_time_diff, 'ban add'), ], fixed_size=18, ) # evil packet, see also 0x794e login_admin.s(0x2726, head=[ at(0, u16, 'packet id'), at(2, u16, 'unused'), at(4, SkewLengthType(u32, 8), 'magic packet length'), ], head_size=8, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) login_char.r(0x2727, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) # 0x2b10, 0x2b11 for (id, cat) in [ (0x2728, login_char.r), (0x2729, login_char.s), ]: cat(id, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), ], head_size=8, repeat=[ at(0, var_name, 'name'), at(32, u32, 'value'), ], repeat_size=36, ) login_char.r(0x272a, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) login_char.s(0x2730, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) login_char.s(0x2731, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'ban not status'), at(7, time32, 'status or ban until'), ], fixed_size=11, ) login_char.s(0x2732, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[ at(0, account_id, 'account id'), at(4, gm1, 'gm level'), ], repeat_size=5, ) login_char.r(0x2740, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_pass, 'old pass'), at(30, account_pass, 'new pass'), ], fixed_size=54, ) login_char.s(0x2741, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'status'), ], fixed_size=7, ) # char map char_map.r(0x2af7, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) char_map.r(0x2af8, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_pass, 'account pass'), at(50, u32, 'unused'), at(54, ip4, 'ip'), at(58, u16, 'port'), ], fixed_size=60, ) char_map.s(0x2af9, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, ) # wtf duplicate v char_map.r(0x2afa, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[ at(0, map_name, 'map name'), ], repeat_size=16, ) # wtf duplicate ^ char_map.s(0x2afa, fixed=[ at(0, u16, 'packet id'), at(2, item_name_id4, 'source item id'), at(6, item_name_id4, 'dest item id'), ], fixed_size=10, ) char_map.s(0x2afb, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'unknown'), at(3, char_name, 'whisper name'), ], fixed_size=27, ) char_map.r(0x2afc, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, char_id, 'char id'), at(10, u32, 'login id1'), at(14, u32, 'login id2'), at(18, ip4, 'ip'), ], fixed_size=22, ) char_map.s(0x2afd, payload=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), at(8, u32, 'login id2'), at(12, time32, 'connect until'), at(16, u16, 'packet tmw version'), at(18, char_key, 'char key'), at(None, char_data, 'char data'), ], payload_size=None, ) char_map.s(0x2afe, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) char_map.r(0x2aff, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, u16, 'users'), ], head_size=6, repeat=[ at(0, char_id, 'char id'), ], repeat_size=4, ) char_map.s(0x2b00, fixed=[ at(0, u16, 'packet id'), at(2, u32, 'users'), ], fixed_size=6, ) char_map.r(0x2b01, payload=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), at(8, char_id, 'char id'), at(12, char_key, 'char key'), at(None, char_data, 'char data'), ], payload_size=None, ) char_map.r(0x2b02, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u32, 'login id1'), at(10, u32, 'login id2'), at(14, ip4, 'ip'), ], fixed_size=18, ) char_map.s(0x2b03, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'unknown'), ], fixed_size=7, ) char_map.s(0x2b04, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, ip4, 'ip'), at(8, u16, 'port'), ], head_size=10, repeat=[ at(0, map_name, 'map name'), ], repeat_size=16, ) char_map.r(0x2b05, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u32, 'login id1'), at(10, u32, 'login id2'), at(14, char_id, 'char id'), at(18, map_name, 'map name'), at(34, u16, 'x'), at(36, u16, 'y'), at(38, ip4, 'map_ip'), at(42, u16, 'map_port'), at(44, sex, 'sex'), at(45, ip4, 'client_ip'), ], fixed_size=49, ) char_map.s(0x2b06, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u32, 'error'), at(10, u32, 'unknown'), at(14, char_id, 'char id'), at(18, map_name, 'map name'), at(34, u16, 'x'), at(36, u16, 'y'), at(38, ip4, 'map_ip'), at(42, u16, 'map_port'), ], fixed_size=44, ) # 0x2720 char_map.r(0x2b0a, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), ], head_size=8, repeat=[at(0, u8, 'c')], repeat_size=1, ) char_map.s(0x2b0b, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, gm, 'gm level'), ], fixed_size=10, ) # 0x2722 char_map.r(0x2b0c, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_email, 'old email'), at(46, account_email, 'new email'), ], fixed_size=86, ) char_map.s(0x2b0d, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, sex, 'sex'), ], fixed_size=7, ) char_map.r(0x2b0e, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, char_name, 'char name'), at(30, u16, 'operation'), at(32, human_time_diff, 'ban add'), ], fixed_size=44, ) char_map.r(0x2b0f, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, char_name, 'char name'), at(30, u16, 'operation'), at(32, u16, 'error'), ], fixed_size=34, ) # 0x2728, 0x2729 for (id, cat) in [ (0x2b10, char_map.r), (0x2b11, char_map.s), ]: cat(id, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), ], head_size=8, repeat=[ at(0, var_name, 'name'), at(32, u32, 'value'), ], repeat_size=36, ) char_map.s(0x2b12, fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), at(6, char_id, 'partner id'), ], fixed_size=10, ) char_map.s(0x2b13, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) char_map.s(0x2b14, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'ban not status'), at(7, time32, 'status or ban until'), ], fixed_size=11, ) char_map.s(0x2b15, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[ at(0, account_id, 'account id'), at(4, gm1, 'gm level'), ], repeat_size=5, ) char_map.r(0x2b16, fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), ], fixed_size=6, ) char_map.r(0x3000, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) char_map.r(0x3001, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, char_name, 'from char name'), at(28, char_name, 'to char name'), ], head_size=52, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) char_map.r(0x3002, fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), at(6, u8, 'flag'), ], fixed_size=7, ) # 0x3803 char_map.r(0x3003, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, char_name, 'char name'), at(28, gm2, 'min gm level'), ], head_size=30, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) # 0x3804 char_map.r(0x3004, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), ], head_size=8, repeat=[ at(0, var_name, 'name'), at(32, u32, 'value'), ], repeat_size=36, ) char_map.r(0x3005, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) char_map.r(0x3010, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) char_map.r(0x3011, payload=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), at(8, storage, 'storage'), ], payload_size=None, ) char_map.r(0x3020, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, party_name, 'party name'), at(30, char_name, 'char name'), at(54, map_name, 'map name'), at(70, u16, 'level'), ], fixed_size=72, ) char_map.r(0x3021, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), ], fixed_size=6, ) char_map.r(0x3022, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, char_name, 'char name'), at(34, map_name, 'map name'), at(50, u16, 'level'), ], fixed_size=52, ) char_map.r(0x3023, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, u16, 'exp'), at(12, u16, 'item'), ], fixed_size=14, ) char_map.r(0x3024, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), ], fixed_size=10, ) char_map.r(0x3025, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, map_name, 'map name'), at(26, u8, 'online'), at(27, u16, 'level'), ], fixed_size=29, ) char_map.r(0x3026, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), ], fixed_size=6, ) char_map.r(0x3027, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, party_id, 'party id'), at(8, account_id, 'account id'), ], head_size=12, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) char_map.r(0x3028, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, char_name, 'char name'), ], fixed_size=34, ) char_map.s(0x3800, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) char_map.s(0x3801, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, char_id, 'whisper id'), at(8, char_name, 'src char name'), at(32, char_name, 'dst char name'), ], head_size=56, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) char_map.s(0x3802, fixed=[ at(0, u16, 'packet id'), at(2, char_name, 'sender char name'), at(26, u8, 'flag'), ], fixed_size=27, ) # 0x3003 char_map.s(0x3803, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, char_name, 'char name'), at(28, gm2, 'min gm level'), ], head_size=30, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) # 0x3004 char_map.s(0x3804, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), ], head_size=8, repeat=[ at(0, var_name, 'name'), at(32, u32, 'value'), ], repeat_size=36, ) char_map.s(0x3810, payload=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), at(8, storage, 'storage'), ], payload_size=None, ) char_map.s(0x3811, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'unknown'), ], fixed_size=7, ) char_map.s(0x3820, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'error'), at(7, party_id, 'party id'), at(11, party_name, 'party name'), ], fixed_size=35, ) char_map.s(0x3821, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, party_id, 'party id'), ], head_size=8, option=[ at(0, party_most, 'party most'), ], option_size=None, ) char_map.s(0x3822, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, u8, 'flag'), ], fixed_size=11, ) char_map.s(0x3823, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, u16, 'exp'), at(12, u16, 'item'), at(14, u8, 'flag'), ], fixed_size=15, ) char_map.s(0x3824, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, char_name, 'char name'), ], fixed_size=34, ) char_map.s(0x3825, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, account_id, 'account id'), at(10, map_name, 'map name'), at(26, u8, 'online'), at(27, u16, 'level'), ], fixed_size=29, ) char_map.s(0x3826, fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, u8, 'flag'), ], fixed_size=7, ) char_map.s(0x3827, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, party_id, 'party id'), at(8, account_id, 'account id'), ], head_size=12, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) # any client any_user.r(0x7530, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) any_user.s(0x7531, fixed=[ at(0, u16, 'packet id'), at(2, version, 'version'), ], fixed_size=10, ) any_user.r(0x7532, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) # login admin login_admin.r(0x7918, fixed=[ at(0, u16, 'packet id'), at(2, u16, 'encryption zero'), at(4, account_pass, 'account pass'), ], fixed_size=28, ) login_admin.s(0x7919, fixed=[ at(0, u16, 'packet id'), at(2, u8, 'error'), ], fixed_size=3, ) login_admin.r(0x7920, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'start account id'), at(6, account_id, 'end account id'), ], fixed_size=10, ) login_admin.s(0x7921, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[ at(0, account_id, 'account id'), at(4, gm1, 'gm level'), at(5, account_name, 'account name'), at(29, sex, 'sex'), at(30, u32, 'login count'), at(34, u32, 'status'), ], repeat_size=38, ) login_admin.r(0x7924, fixed=[ at(0, u16, 'packet id'), at(2, item_name_id4, 'source item id'), at(6, item_name_id4, 'dest item id'), ], fixed_size=10, ) login_admin.s(0x7925, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) login_admin.r(0x7930, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_pass, 'password'), at(50, sex_char, 'sex'), at(51, account_email, 'email'), ], fixed_size=91, ) login_admin.s(0x7931, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x7932, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), ], fixed_size=26, ) login_admin.s(0x7933, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x7934, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_pass, 'password'), ], fixed_size=50, ) login_admin.s(0x7935, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x7936, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, u32, 'status'), at(30, seconds, 'error message'), ], fixed_size=50, ) login_admin.s(0x7937, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), at(30, u32, 'status'), ], fixed_size=34, ) login_admin.r(0x7938, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) login_admin.s(0x7939, head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), ], head_size=4, repeat=[ at(0, ip4, 'ip'), at(4, u16, 'port'), at(6, server_name, 'name'), at(26, u16, 'users'), at(28, u16, 'maintenance'), at(30, u16, 'is_new'), ], repeat_size=32, ) login_admin.r(0x793a, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_pass, 'password'), ], fixed_size=50, ) login_admin.s(0x793b, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x793c, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, sex_char, 'sex'), ], fixed_size=27, ) login_admin.s(0x793d, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x793e, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, gm1, 'gm level'), ], fixed_size=27, ) login_admin.s(0x793f, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x7940, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_email, 'email'), ], fixed_size=66, ) login_admin.s(0x7941, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) # this packet is insane login_admin.r(0x7942, head=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, SkewLengthType(u16, 28), 'magic packet length'), ], head_size=28, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) login_admin.s(0x7943, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x7944, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), ], fixed_size=26, ) login_admin.s(0x7945, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x7946, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) login_admin.s(0x7947, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, ) login_admin.r(0x7948, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, time32, 'valid until'), ], fixed_size=30, ) login_admin.s(0x7949, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), at(30, time32, 'valid until'), ], fixed_size=34, ) login_admin.r(0x794a, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, time32, 'ban until'), ], fixed_size=30, ) login_admin.s(0x794b, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), at(30, time32, 'ban until'), ], fixed_size=34, ) login_admin.r(0x794c, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, human_time_diff, 'ban add'), ], fixed_size=38, ) login_admin.s(0x794d, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), at(30, time32, 'ban until'), ], fixed_size=34, ) # evil packet (see also 0x2726) login_admin.r(0x794e, head=[ at(0, u16, 'packet id'), at(2, u16, 'unused'), at(4, SkewLengthType(u32, 8), 'magic packet length'), ], head_size=8, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) login_admin.s(0x794f, fixed=[ at(0, u16, 'packet id'), at(2, u16, 'error'), ], fixed_size=4, ) login_admin.r(0x7950, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, human_time_diff, 'valid add'), ], fixed_size=38, ) login_admin.s(0x7951, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), at(30, time32, 'valid until'), ], fixed_size=34, ) login_admin.r(0x7952, fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), ], fixed_size=26, ) # this packet is insane login_admin.s(0x7953, head=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, gm1, 'gm level'), at(7, account_name, 'account name'), at(31, sex, 'sex'), at(32, u32, 'login count'), at(36, u32, 'state'), at(40, seconds, 'error message'), at(60, millis, 'last login string'), at(84, str16, 'ip string'), at(100, account_email, 'email'), at(140, time32, 'connect until'), at(144, time32, 'ban until'), at(148, SkewLengthType(u16, 150), 'magic packet length'), ], head_size=150, repeat=[ at(0, u8, 'c'), ], repeat_size=1, ) login_admin.r(0x7954, fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, ) login_admin.r(0x7955, fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, ) # teardown ctx.dump() if __name__ == '__main__': main()