clientbot/client.py

2624 lines
100 KiB
Python
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# script to emulate client khanat
#
# Copyright (C) 2019 AleaJactaEst
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Ex.: ./client.py --khanat-host 172.17.0.3 -d --size-buffer-file 10241024
# Modifier les droits pour les nouveaux joueurs (accès à tout)
# mysql -u root -e "use nel_ams_lib;UPDATE settings SET Value = 7 WHERE settings.Setting = 'Domain_Auto_Add';"
import argparse
import http.client
import crypt
import logging
import os
import os.path
import sys
import urllib.request
import urllib.parse
import tempfile
from enum import IntEnum
from ctypes import *
import re
import random
import lzma
import socket
import struct
import xml.etree.ElementTree as ET
import hashlib
import time
import signal
INVALID_SLOT = 0xff
class BitStream():
def __init__(self):
self._pos = 0
self._read = 0
self._tampon = []
def needRead(self):
return self._pos - self._read
def sizeData(self):
return self._pos
def sizeRead(self):
return self._read
# ------------------------------------
def internalSerial(self, value, nbits):
if nbits == 0:
return
elif nbits > 32:
raise "Out of range"
pos = self._pos % 8
if pos == 0:
self._tampon.append(0)
# print(">", pos, value)
value = c_uint32(value).value
if nbits != 32:
mask = (1 << nbits) - 1;
v = value & mask;
else:
v = value;
_FreeBits = 8 - (self._pos % 8)
if nbits > _FreeBits:
#print(">A")
self._tampon[-1] |= (v >> ( nbits - _FreeBits))
self._pos += _FreeBits
self.internalSerial( v , nbits - _FreeBits)
else:
#print(">B")
self._tampon[-1] |= (v << ( _FreeBits - nbits))
self._pos += nbits
def pushBool(self, valeur):
if valeur:
v = 1
else:
v = 0
self.internalSerial(v, 1)
def pushUint32(self, valeur):
self.internalSerial(valeur, 32)
def pushSint32(self, valeur):
self.internalSerial(valeur, 32)
def pushUint16(self, valeur):
self.internalSerial(valeur, 16)
def pushSint16(self, valeur):
self.internalSerial(valeur, 16)
def pushUint8(self, valeur):
self.internalSerial(valeur, 8)
def pushSint8(self, valeur):
self.internalSerial(valeur, 8)
def pushUint64(self, valeur):
self.internalSerial(valeur, 32)
self.internalSerial(valeur >> 32, 32)
def pushSint64(self, valeur):
self.internalSerial(valeur, 32)
self.internalSerial(valeur >> 32, 32)
def pushFloat(self, valeur):
v = c_float(valeur).value
v1 = struct.pack('f', v)
v2 = struct.unpack('<i',v1)[0]
self.internalSerial(v2, 32)
def pushDouble(self, valeur):
v = c_double(valeur).value
v1 = struct.pack('d', v)
v2 = struct.unpack('<Q',v1)[0]
#self.internalSerial(v2, 32)
self.internalSerial(v2, 32)
self.internalSerial(v2 >> 32, 32)
def pushChar(self, valeur):
v = ord(valeur)
self.internalSerial(v, 8)
def pushString(self, valeur):
#size=len(valeur)
#self.internalSerial(size, 32)
self.pushUint32(len(valeur))
for x in valeur:
self.pushChar(x)
#y = ord(x)
#self.internalSerial(y, 8)
def pushArrayUint8(self, valeur, size):
' ex.: pushArrayChar([0,1,3,4]) '
for i in valeur:
self.pushUint8(i)
# ------------------------------------
def readSerial(self, nbits):
if nbits == 0:
return
elif nbits > 32:
raise "Out of range"
if self._read + nbits > self._pos:
raise "Stream Overflow"
value = 0
pos = self._read // 8
_FreeBits = 8 - (self._read % 8)
v = self._tampon[pos] & ((1 << _FreeBits) - 1)
if nbits > _FreeBits:
value |= (v << (nbits-_FreeBits))
self._read += _FreeBits
value |= self.readSerial(nbits - _FreeBits)
else:
value |= (v >> (_FreeBits-nbits))
self._read += nbits
return value
def readBool(self):
v = self.readSerial(1)
if v != 0:
return True
else:
return False
def readUint32(self):
v = self.readSerial(32)
return v
def readSint32(self):
v = self.readSerial(32)
return c_int32(v).value
def readUint16(self):
v = self.readSerial(16)
return v
def readSint16(self):
v = self.readSerial(16)
return c_int16(v).value
def readUint8(self):
v = self.readSerial(8)
return v
def readSint8(self):
v = self.readSerial(8)
return c_int8(v).value
def readUint64(self):
v = self.readSerial(32)
v1 = self.readSerial(32)
v2 = v | (v1 << 32)
return v2
def readSint64(self):
v = self.readSerial(32)
v1 = self.readSerial(32)
v2 = v | (v1 << 32)
return c_int64(v2).value
def readFloat(self):
v = self.readSerial(32)
v1 = struct.pack('I', v)
v2 = struct.unpack('<f',v1)[0]
return v2
def readDouble(self):
v = self.readSerial(32)
v1 = struct.pack('I', v)
w = self.readSerial(32)
w1 = struct.pack('I', w)
x = v1 + w1
x1 = struct.unpack('<d', x)[0]
return x1
def readChar(self):
v = self.readUint8()
return chr(v)
def readString(self):
tmp = ''
_size = self.readUint32()
while _size > 0:
x = self.readChar()
tmp += x
_size -= 1
return tmp
def readArrayUint8(self, size):
ret = []
for i in range(0, size):
ret.append(self.readUint8())
return ret
# ------------------------------------
def __str__(self):
return ''.join([ chr(x) for x in self._tampon])
def message(self):
# return str(self._pos) + ':' + str(self._tampon)
return str(self._pos) + ':' + '.'.join([ format(x, "02x") for x in self._tampon])
def toBytes(self):
return bytes( self._tampon )
def fromBytes(self, data):
self._read = 0
self._tampon = [int(x) for x in data]
self._pos = len(self._tampon) * 8
def showLastData(self):
ret = ""
readBefore = self._read
while self._read < self._pos:
if self._pos - self._read >= 8:
data = self.readUint8()
else:
data = self.readSerial(self._pos - self._read)
if ret != "":
ret += "."
ret += hex(data)
self._read = readBefore
return ret
def TestBitStream():
a = BitStream()
a.pushBool(True)
a.pushBool(False)
a.pushBool(True)
a.pushBool(True)
a.pushUint32(1234567890)
a.pushSint32(-1234567890)
a.pushUint16(12345)
a.pushSint16(-12345)
a.pushUint8(123)
a.pushSint8(-123)
a.pushFloat(-3.3999999521443642e+38) #-3.4E+38) # 1.2339999675750732)
a.pushDouble(-1.7E+308)
a.pushUint64(16045690709418696365)
a.pushSint64(-1)
a.pushChar('a')
a.pushString("Test A Faire")
print('raw:', a)
print("-" * 80)
print(a.readBool())
print(a.readBool())
print(a.readBool())
print(a.readBool())
print(a.readUint32())
print(a.readSint32())
print(a.readUint16())
print(a.readSint16())
print(a.readUint8())
print(a.readSint8())
print(a.readFloat())
print(a.readDouble())
print(a.readUint64())
print(a.readSint64())
print(a.readChar())
print(a.readString())
print(a.toBytes())
print("-" * 80)
b = BitStream()
b.fromBytes(a.toBytes())
print(b.readBool())
print(b.readBool())
print(b.readBool())
print(b.readBool())
print(b.readUint32())
print(b.readSint32())
print(b.readUint16())
print(b.readSint16())
print(b.readUint8())
print(b.readSint8())
print(b.readFloat())
print(b.readDouble())
print(b.readUint64())
print(b.readSint64())
print(b.readChar())
print(b.readString())
print(b.toBytes())
NL_BITLEN = 32
class CBitSet:
def __init__(self):
self.data = self.resize(1024)
self.NumBits = 0
self.MaskLast = 0
def resize(self, numBits):
self.data = [ 0 for _ in range(0, (numBits +NL_BITLEN - 1) // NL_BITLEN) ]
self.NumBits = numBits
nLastBits = self.NumBits & (NL_BITLEN-1)
if nLastBits == 0:
self.MaskLast = ~0
else:
self.MaskLast = (1 << nLastBits)-1
self.clearAll()
def clearData(self):
self.data = []
self.NumBits = 0
self.MaskLast = 0
def clearAll(self):
for i in range(0, len(self.data)):
self.data[i] = 0
def set(self, bitNumber, value):
mask = bitNumber & (NL_BITLEN-1)
mask = 1 << mask
if value:
self.data[bitNumber >> 5] |= mask
else:
self.data[bitNumber >> 5] &= ~mask
def get(self, bitNumber):
mask= bitNumber&(NL_BITLEN-1);
mask= 1<<mask;
return self.data[bitNumber >> 5] & mask != 0
def setBit(self, bitNumber):
self.set(bitNumber, True)
def clearBit(self, bitNumber):
self.set(bitNumber, False)
def __str__(self):
return '.'.join([hex(x) for x in self.data])
def writeSerial(self, msgout):
# v = 0 # currentVersion
# if v >= 0xff:
# msgout.pushUint8(0xff)
# msgout.pushUint8(v)
# else:
# msgout.pushUint8(v)
msgout.pushUint8(0) # currentVersion =0
msgout.pushUint32(self.NumBits)
msgout.pushUint32(len(self.data)) # il est lié à 'self.NumBits' dommage que l'on envoie celui-la
for x in self.data:
msgout.pushUint32(x)
def TestCBitSet():
cBitSet = CBitSet()
cBitSet.resize(1024)
cBitSet.set(1, True)
cBitSet.set(3, True)
cBitSet.set(2, False)
cBitSet.set(13, True)
cBitSet.set(128, True)
cBitSet.set(1023, True)
print(cBitSet)
print(cBitSet.get(3))
cBitSet.set(3, False)
print(cBitSet)
print(cBitSet.get(3))
def getTextMD5(dataRawXml):
log = logging.getLogger('myLogger')
dataNew = ''
for data in dataRawXml:
if data != '\r': # '\015' ignore caractère \r\n =>
dataNew += data
else:
log.debug("***** data:%d" % (ord(data)))
m = hashlib.md5()
m.update(dataNew.encode('utf-8'))
#print(m.hexdigest())
#print(m.digest())
return m.digest()
class CFileChild():
def __init__(self, name, pos, size):
self.name = name
self.pos = pos
self.size = size
def __str__(self):
return self.name + '(pos:' + str(self.pos) + ', size:' + str(self.size) + ')'
class CFileList():
def __init__(self, name, fullpath):
self.name = name
self.fullpath = fullpath
self.child = []
def addchild(self, name, pos, size):
child = CFileChild(name, pos, size)
self.child.append(child)
def __str__(self):
return self.name + '[' + ', '.join([str(x) for x in self.child]) + ']'
class CFileContainer():
def __init__(self):
self.log = logging.getLogger('myLogger')
self.list = []
def addSearchPath(self, path):
if not path:
return
self.log.debug("read path:" + str(path))
onlyfiles = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
self.log.debug("read files:" + ','.join(onlyfiles))
for filename in onlyfiles:
extension = os.path.splitext(filename)[1]
if extension == '.bnp':
# Container for multi file
fullpath = os.path.join(path, filename)
size = os.path.getsize(fullpath)
data = CFileList(filename, fullpath)
with open(fullpath, 'rb') as fp:
fp.seek(size-4)
nOffsetFromBeginning = int.from_bytes(fp.read(4), byteorder='little', signed=False)
self.log.debug("[%s] nOffsetFromBeginning:%u" % (filename, nOffsetFromBeginning))
fp.seek(nOffsetFromBeginning)
nNbFile = int.from_bytes(fp.read(4), byteorder='little', signed=False)
self.log.debug("[%s] nNbFile:%u" % (filename, nNbFile))
for i in range(0, nNbFile):
nStringSize = int.from_bytes(fp.read(1), byteorder='little', signed=False)
FileName = fp.read(nStringSize).decode()
nFileSize2 = int.from_bytes(fp.read(4), byteorder='little', signed=False)
nFilePos = int.from_bytes(fp.read(4), byteorder='little', signed=False)
self.log.debug("[%s] (%d) sizestring:%d file:%s size2:%d pos:%d" % (filename, i, nStringSize, FileName, nFileSize2, nFilePos))
data.addchild(FileName, nFilePos, nFileSize2)
fp.close()
self.list.append(data)
def search(self, name):
for x in self.list:
for y in x.child:
if y.name == name:
self.log.debug("file:%s child:%s pos:%d size:%d", x.name, y.name, y.pos, y.size)
return x.fullpath, y.pos, y.size
self.log.debug('-'*80)
return None, None, None
def getdata(self, name):
fullpath, pos, size = self.search(name)
self.log.debug("file:%s pos:%d size:%d", fullpath, pos, size)
data = None
with open(fullpath, 'rb') as fp:
fp.seek(pos)
data = fp.read(size)
fp.close()
return data
class TConnectionState(IntEnum):
NotInitialised = 0 # nothing happened yet
NotConnected = 1 # init() called
Authenticate = 2 # connect() called, identified by the login server
Login = 3 # connecting to the frontend, sending identification
Synchronize = 4 # connection accepted by the frontend, synchronizing
Connected = 5 # synchronized, connected, ready to work
Probe = 6 # connection lost by frontend, probing for response
Stalled = 7 # server is stalled
Disconnect = 8 # disconnect() called, or timeout, or connection closed by frontend
Quit = 9 # quit() called
class TActionCode(IntEnum):
ACTION_POSITION_CODE = 0
ACTION_GENERIC_CODE = 1
ACTION_GENERIC_MULTI_PART_CODE = 2
ACTION_SINT64 = 3
ACTION_SYNC_CODE = 10
ACTION_DISCONNECTION_CODE = 11
ACTION_ASSOCIATION_CODE = 12
ACTION_LOGIN_CODE = 13
ACTION_TARGET_SLOT_CODE = 40
ACTION_DUMMY_CODE = 99
class CLFECOMMON(IntEnum):
SYSTEM_LOGIN_CODE = 0
SYSTEM_SYNC_CODE = 1
SYSTEM_ACK_SYNC_CODE = 2
SYSTEM_PROBE_CODE = 3
SYSTEM_ACK_PROBE_CODE = 4
SYSTEM_DISCONNECTION_CODE = 5
SYSTEM_STALLED_CODE = 6
SYSTEM_SERVER_DOWN_CODE = 7
SYSTEM_QUIT_CODE = 8
SYSTEM_ACK_QUIT_CODE = 9
NumBitsInLongAck = 512
class Card(IntEnum):
BEGIN_TOKEN = 0
END_TOKEN = 1
SINT_TOKEN = 2
UINT_TOKEN = 3
FLOAT_TOKEN = 4
STRING_TOKEN = 5
FLAG_TOKEN = 6
EXTEND_TOKEN = 7
class TType(IntEnum):
STRUCT_BEGIN = 0
STRUCT_END = 1
FLAG = 2
SINT32 = 3
UINT32 = 4
FLOAT32 = 5
STRING = 6
SINT64 = 7
UINT64 = 8
FLOAT64 = 9
EXTEND_TYPE = 10
NB_TYPE = 11
class TExtendType:
ET_SHEET_ID = 0
ET_64_BIT_EXTENDED_TYPES = 0x80000000
ET_ENTITY_ID = 0x80000000 # ET_ENTITY_ID = ET_64_BIT_EXTENDED_TYPES
class CBNPFileVersion:
def __init__(self):
self.VersionNumber = None
self.FileTime = None
self.FileSize = None
self.v7ZFileSize = None
self.PatchSize = None
self.HashKey = []
def __str__(self):
return "VersionNumber:" + str(self.VersionNumber) + ", FileTime:" + str(self.FileTime) + ", FileSize:" + str(self.FileSize) + ", 7ZFileSize:" + str(self.v7ZFileSize) + ", PatchSize:" + str(self.PatchSize) + ", HashKey:" + str(self.HashKey)
class CBNPFile:
def __init__(self):
self.FileName = None
self.Versions = []
self.IsIncremental = False
def __str__(self):
return str(self.FileName) +' (' + ', '.join( [str(x) for x in self.Versions]) + ')'
def update(self, FileName):
self.FileName = FileName
class CBNPCategorySet:
def __init__(self):
self._Name = ""
self._IsOptional = False
self._UnpackTo = ""
self._IsIncremental = False
self._CatRequired = ""
self._Hidden = False
self._Files = []
def __str__(self):
return self._Name + ' (IsOptional:' + str(self._IsOptional) + ', UnpackTo:' + self._UnpackTo + ', IsIncremental:' + str(self._IsIncremental) + ', CatRequired:' + self._CatRequired + ', Hidden:' + str(self._Hidden) + ', Files:' + str(self._Files) + ')'
# #####################################################
# persistent_data.h:140 # struct CArg
# #####################################################
class CArgV1(Structure):
_fields_ = [("i32_1", c_uint),
("i32_2", c_uint)]
class CArgV2(Structure):
_fields_ = [("ex32_1", c_uint),
("ex32_2", c_uint)]
class CArgV3(Union):
_fields_ = [("ex32", CArgV2),
("ExData32", c_uint),
("ExData64", c_ulong)]
class CArgV4(Structure):
_fields_ = [("ExType", c_uint),
("ex", CArgV3)]
class CArgV5(Union):
_fields_ = [("i", CArgV1),
("ii32", c_int),
("ii64", c_long),
("i32", c_uint),
("i64", c_ulong),
("f32", c_float),
("f64", c_double),
("ex", CArgV4)]
class CArg:
# union
# {
# struct
# {
# uint32 i32_1;
# uint32 i32_2;
# } i;
#
# sint32 i32;
# sint64 i64;
# float f32;
# double f64;
#
# struct
# {
# uint32 ExType;
# union
# {
# struct
# {
# uint32 ex32_1;
# uint32 ex32_2;
# };
#
# uint32 ExData32;
# uint64 ExData64;
# } ex;
# } ex;
# } _Value;
def __init__(self):
self._value = CArgV5()
self._value.ii64 = 0
self._value.i64 = 0
self._type = 0
self._string = 0
self._type64 = False
def read_Type(self):
return self._type
def write_Type(self, value):
self._type = value
def write_Type64(self, value):
self._type64 = value
def read_String(self):
return self._string
def write_String(self, value):
self._string = value
def read_i32_1(self):
return self._value.i.i32_1
def write_i32_1(self, value):
self._value.i.i32_1 = value
def read_i32_2(self):
return self._value.i.i32_2
def write_i32_2(self, value):
self._value.i.i32_2 = value
def read_i32(self):
return self._value.i32
def write_i32(self, value):
self._value.i32 = value
def read_i64(self):
return self._value.i64
def write_i64(self, value):
self._value.i64 = value
def read_f32(self):
return self._value.f32
def write_f32(self, value):
self._value.f32 = value
def read_f64(self):
return self._value.f64
def write_f64(self, value):
self._value.f64 = value
def read_ExType(self):
return self._value.ex.ExType
def write_ExType(self, value):
self._value.ex.ExType = value
def read_ex32_1(self):
return self._value.ex.ex.ex32.ex32_1
def write_ex32_1(self, value):
self._value.ex.ex.ex32.ex32_1 = value
def read_ex32_2(self):
return self._value.ex.ex.ex32.ex32_2
def write_ex32_2(self, value):
self._value.ex.ex.ex32.ex32_2 = value
def read_ExData32(self):
return self._value.ex.ex.ExData32
def write_ExData32(self, value):
self._value.ex.ex.ExData32 = value
def read_ExData64(self):
return self._value.ex.ex.ExData64
def write_ExData64(self, value):
self._value.ex.ex.ExData64 = value
def isExtended(self):
if self._type == TType.EXTEND_TYPE:
return True
elif self._type == TType.STRUCT_BEGIN:
self.log.error("Can't extract a value from a structure delimiter")
sys.exit(2)
elif self._type == TType.STRUCT_END:
self.log.error("Can't extract a value from a structure delimiter")
sys.exit(2)
return False
def isFlag(self):
if self._type == TType.FLAG:
return True
else:
return False
def asUint(self):
if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END:
self.log.error("Can't extract a value from a structure delimiter")
sys.exit(2)
elif self._type == TType.SINT32:
return self.read_i32()
elif self._type == TType.UINT32:
return self.read_i32()
elif self._type == TType.SINT64:
return self.read_i64()
elif self._type == TType.UINT64:
return self.read_i64()
elif self._type == TType.FLOAT32:
return self.read_i32()
elif self._type == TType.FLOAT64:
return self.read_i64()
elif self._type == TType.STRING:
return int(self._string)
elif self._type == TType.FLAG:
return "1"
elif self._type == TType.EXTEND_TYPE:
if self.read_ExType() == TExtendType.ET_SHEET_ID:
return self.read_ExData32()
elif self.read_ExType() == TExtendType.ET_ENTITY_ID:
return self.read_ExData64()
log = logging.getLogger('myLogger')
log.error("This should never happen!")
sys.exit(2)
def __str__(self):
log = logging.getLogger('myLogger')
log.debug(self._type)
if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END:
return ''
elif self._type64:
# To be confirm for extend
return str(self.read_ExData64())
elif self._type == TType.SINT32:
return str(self.read_i32())
elif self._type == TType.UINT32:
return str(self.read_i32())
elif self._type == TType.SINT64:
return str(self.read_i64())
elif self._type == TType.UINT64:
return str(self.read_i64())
elif self._type == TType.FLOAT32:
return str(self.read_i32())
elif self._type == TType.FLOAT64:
return str(self.read_i64())
elif self._type == TType.STRING:
return self._string
elif self._type == TType.FLAG:
return "1"
return '?'
def asSint(self):
self.log.error("TODO")
sys.exit(2)
def asFloat(self):
self.log.error("TODO")
sys.exit(2)
def asDouble(self):
self.log.error("TODO")
sys.exit(2)
def asString(self):
if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END:
self.log.error("Can't extract a value from a structure delimiter")
sys.exit(2)
elif self._type == TType.SINT32:
return str(self.read_ii32())
elif self._type == TType.UINT32:
return str(self.read_i32())
elif self._type == TType.SINT64:
return str(self.read_ii64())
elif self._type == TType.UINT64:
return str(self.read_i64())
elif self._type == TType.FLOAT32:
return str(self.read_f32())
elif self._type == TType.FLOAT64:
return str(self.read_f64())
elif self._type == TType.STRING:
return self._string
elif self._type == TType.FLAG:
return "1"
elif self._type == TType.EXTEND_TYPE:
self.log.error("TODO")
sys.exit(2)
# switch(_Value.ExType)
# {
# case ET_SHEET_ID:
# {
# NLMISC::CSheetId sheetId(_Value.ExData32);
# return sheetId.toString(true);
# }
# case ET_ENTITY_ID:
# {
# NLMISC::CEntityId entityId(_Value.ExData64);
# return entityId.toString();
# }
# default:
# break;
# }
self.log.error("This should never happen!")
sys.exit(2)
def asUCString(self):
self.log.error("TODO")
sys.exit(2)
def asEntityId(self):
self.log.error("TODO")
sys.exit(2)
def asSheetId(self):
self.log.error("TODO")
sys.exit(2)
def typeName(self):
self.log.error("TODO")
sys.exit(2)
# #####################################################
#
# #####################################################
class CPersistentDataRecord:
def __init__(self, log):
self.log = log
self.TokenTable = []
self.ArgTable = []
self.StringTable = [ ]
self.ReadingStructStack = []
self.offsetToken = 0
self.ArgOffset = 0
self.version = 0
self.totalSize = 0
self.tokenCount = 0
self.argCount = 0
self.stringCount = 0
self.stringsSize = 0
self.CBNPFile = []
self.Categories = []
def show(self):
for x in self.CBNPFile:
self.log.debug("File:%s" % str(x))
for x in self.Categories:
self.log.debug("Categorie:%s" % str(x))
# ---------------- Manipulate Token ----------------
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
# | Token ID | Token Type |
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
def token2Type(self, token, extend): # persistent_data_inline.h:1102 CPersistentDataRecord::CArg::TType CPersistentDataRecord::CArg::token2Type(uint32 token,bool extend)
self.log.debug("token:%d, extend:%d" % (token, extend))
if token == Card.BEGIN_TOKEN:
return TType.STRUCT_BEGIN
elif token == Card.END_TOKEN:
return TType.STRUCT_END
elif token == Card.FLAG_TOKEN:
return TType.FLAG
elif token == Card.SINT_TOKEN:
if extend:
return TType.SINT64
else:
return TType.SINT32
elif token == Card.UINT_TOKEN:
if extend:
return TType.UINT64
else:
return TType.UINT32
elif token == Card.FLOAT_TOKEN:
if extend:
return TType.FLOAT64
else:
return TType.FLOAT32
elif token == Card.STRING_TOKEN:
if extend:
return TType.EXTEND_TYPE
else:
return TType.STRING
self.log.error('This should never happen!')
sys.exit(2)
def type2Token(self, type): # persistent_data_inline.h:1118 CPersistentDataRecord::TToken CPersistentDataRecord::CArg::type2Token(uint32 type)
self.log.debug("type: %d" %(type))
if type == TType.STRUCT_BEGIN:
return Card.BEGIN_TOKEN
elif type == TType.STRUCT_END:
return Card.END_TOKEN
elif type == TType.FLAG:
return Card.FLAG_TOKEN
elif type == TType.SINT32:
return Card.SINT_TOKEN
elif type == TType.UINT32:
return Card.UINT_TOKEN
elif type == TType.FLOAT32:
return Card.FLOAT_TOKEN
elif type == TType.STRING:
return Card.STRING_TOKEN
elif type == TType.SINT64:
return Card.SINT_TOKEN
elif type == TType.UINT64:
return Card.UINT_TOKEN
elif type == TType.FLOAT64:
return Card.FLOAT_TOKEN
elif type == TType.EXTEND_TYPE:
return Card.STRING_TOKEN
self.log.error('This should never happen!')
sys.exit(2)
def peekNextToken(self):
token = self.TokenTable[self.offsetToken]
self.log.debug("[%d] token:%d" %(self.offsetToken, token))
return token // 8 # persistent_data_inline.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() # _TokenTable[_TokenOffset]>>3;
def peekNextTokenType(self): # persistent_data_limit.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() const
self.log.debug("peekNextTokenType - old offset token:%d" % self.offsetToken)
if self.isEndOfData():
self.log.error('Attempt to read past end of input data')
sys.exit(2)
token = self.TokenTable[self.offsetToken]
tokenType = token & 7
if tokenType == Card.EXTEND_TOKEN:
if self.offsetToken + 1 > self.tokenCount:
self.log.error('Attempt to read past end of input data')
sys.exit(2)
tokenType = self.TokenTable[self.offsetToken+1]
self.log.debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType))
return self.token2Type(tokenType, True)
self.log.debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType))
return self.token2Type(tokenType, False)
def isEndOfData(self):
if self.offsetToken == self.tokenCount:
return True
return False
def isEndOfStruct(self):
if self.isEndOfData():
self.log.debug("isEndOfData")
return True
elif len(self.ReadingStructStack) == 0:
self.log.debug("ReadingStructStack")
return False
elif self.peekNextTokenType() != TType.STRUCT_END:
self.log.debug("peekNextTokenType != TType.STRUCT_END")
return False
elif self.ReadingStructStack[-1] != self.peekNextToken():
self.log.error("Opening and closing structure tokens don't match")
sys.exit(2)
self.log.debug("isEndOfStruct")
return True
def isStartOfStruct(self):
if self.peekNextTokenType() == TType.STRUCT_BEGIN:
return True
return False
def popStructBegin(self, token):
if self.peekNextToken() != token:
self.log.error('Attempting to enter a structure with the wrong delimiting token')
sys.exit(2)
if self.peekNextTokenType() != TType.STRUCT_BEGIN:
self.log.error('Attempting to leave a structure with the wrong delimiting token type')
sys.exit(2)
self.ReadingStructStack.append(token)
self.offsetToken += 1
def popStructEnd(self, token):
if len(self.ReadingStructStack) == 0:
self.log.error('Attempting to pop end of a structure with nothing left in the open structure stack')
sys.exit(2)
nextToken = self.peekNextToken()
topToken = self.ReadingStructStack[-1]
if topToken != token:
self.log.error('Attempting to pop end of a structure with the wrong delimiting token')
sys.exit(2)
if nextToken != token:
self.log.error('Attempting to pop end of a structure with the wrong delimiting token')
sys.exit(2)
if self.peekNextTokenType() != TType.STRUCT_END:
self.log.error('Attempting to leave a structure with the wrong delimiting token type')
sys.exit(2)
del self.ReadingStructStack[-1]
self.offsetToken += 1
# ---------------- Manipulate StringTable ----------------
def lookupString(self, idx):
if idx >= self.stringCount:
self.log.error("Attempting to access past end of string table")
sys.exit(2)
return self.StringTable[idx]
# ---------------- Manipulate Arg ----------------
def peekNextArg(self): # persistent_data_limit.h:339 CPersistentDataRecord::peekNextArg(CPersistentDataRecord::CArg& result) const
_type = self.peekNextTokenType()
result = CArg()
result.write_Type(_type)
result.write_Type64(False)
self.log.debug("peekNextArg - Type:%d ArgOffset:%d" % (_type, self.ArgOffset))
if result.isExtended():
self.log.debug("Extended")
result.write_i32_1(self.ArgTable[self.ArgOffset])
result.write_i32_2(self.ArgTable[self.ArgOffset+1])
if result.read_Type() == TType.EXTEND_TYPE and result.read_ExType() == TExtendType.ET_64_BIT_EXTENDED_TYPES:
result.write_ex32_2(self.ArgTable[self.ArgOffset+2]);
result.write_Type64(True)
elif not result.isFlag():
# result._Value.i32_1 = _ArgTable[_ArgOffset];
result.write_i32_1(self.ArgTable[self.ArgOffset])
self.log.debug("peekNextArg - id :%d" % result.read_i32_1())
if result.read_Type() == TType.STRING:
result.write_String(self.lookupString(result.read_i32_1()))
self.log.debug("peekNextArg - String:%s" % result.read_String())
return result
def popNextArg(self, token): # persistent_data_limit.h:414 CPersistentDataRecord::popNextArg(TToken token,CPersistentDataRecord::CArg& result)
result = self.peekNextArg()
if result.isFlag():
self.offsetToken += 1
elif result.isExtended():
self.ArgOffset += 2
self.offsetToken += 2
if result.read_Type() == TType.EXTEND_TYPE and result.read_ExType() == TExtendType.ET_64_BIT_EXTENDED_TYPES:
self.ArgOffset += 1
self.offsetToken += 1
else:
self.ArgOffset += 1
self.offsetToken += 1
self.log.debug("popNextArg - Arg:%d", result.read_i32_1())
return result
def popString(self, token):
TempArg = self.popNextArg(token)
return TempArg.asString()
def popUint32(self, token):
TempArg = self.popNextArg(token)
return TempArg.asUint()
def popBool(self, token):
TempArg = self.popNextArg(token)
return TempArg.asUint() != 0
# ---------------- Read Data ----------------
def readFromBinFile(self, filename): # persistent_data.cpp:835 # bool CPersistentDataRecord::fromBuffer(const char *src, uint32 bufferSize)
self.log.debug('Read Bin File %s' % filename)
with open(filename, "rb") as fp:
buffer = fp.read()
fp.close()
self.version = int.from_bytes(buffer[0:4], byteorder='little', signed=False)
self.totalSize = int.from_bytes(buffer[4:8], byteorder='little', signed=False)
self.tokenCount = int.from_bytes(buffer[8:12], byteorder='little', signed=False)
self.argCount = int.from_bytes(buffer[12:16], byteorder='little', signed=False)
self.stringCount = int.from_bytes(buffer[16:20], byteorder='little', signed=False)
self.stringsSize = int.from_bytes(buffer[20:24], byteorder='little', signed=False)
offset = 24
self.log.debug("version:%d, totalSize:%d, tokenCount:%d, argCount:%d, stringCount:%d, stringsSize:%d" % (self.version, self.totalSize, self.tokenCount, self.argCount, self.stringCount, self.stringsSize))
if len(buffer) != self.totalSize:
self.log.error("Failed to parse buffer due to invalid header (file:%s, size:%d, size define:%d)" % (filename, len(buffer), self.totalSize ))
sys.exit(2)
if self.version > 0:
self.log.error("PDR ERROR: Wrong file format version! (file:%s, version:%d)" % (filename, self.version))
sys.exit(2)
if (self.stringCount != 0 and self.stringsSize == 0) or (self.stringCount == 0 and self.stringsSize != 0):
self.log.error("PDR ERROR: Invalid string table parameters! (file:%s, stringCount:%d, stringsSize:%d)" % (filename, self.stringCount, self.stringsSize))
sys.exit(2)
# i = offset+tokenCount*sizeof(TToken)+argCount*sizeof(uint32)+stringsSize
i = offset + self.tokenCount * 2 + self.argCount * 4 + self.stringsSize;
if self.totalSize != i:
self.log.error("PDR ERROR: Invalid source data (file:%s, totalSize:%d != datasize:%s)" % (filename, self.totalSize, i))
sys.exit(2)
# READ the tokens
self.TokenTable = []
for i in range(0, self.tokenCount):
tmp = int.from_bytes(buffer[offset:offset+2], byteorder='little', signed=False)
self.log.debug("token %5d => %3d id:%3d type:%d" %(i, tmp, tmp // 8, tmp & 7))
self.TokenTable.append(tmp)
offset += 2
# READ the arguments
self.ArgTable = []
for i in range(0, self.argCount):
tmp = int.from_bytes(buffer[offset:offset+4], byteorder='little', signed=False)
self.ArgTable.append(tmp)
offset += 4
# READ the string table data
if self.stringsSize != 0:
chaine = ''
self.StringTable = [ ]
while offset < self.totalSize:
car = buffer[offset:offset+1].decode()
if car != '\0':
chaine += car
else:
self.StringTable.append(chaine)
chaine = ''
offset += 1
self.log.debug(self.StringTable)
if chaine != '':
self.log.error("PDR ERROR: Too few strings found in string table (file:%s)" % (filename))
sys.exit(2)
self.log.debug("Red %s" % filename)
def decrypt_token(self):
i = 0
lvl = 0
posArg = 0
extend = False
extend64 = False
result = CArg()
print("^ Position ^ Token ^")
for value in self.TokenTable:
print("| %5d | %3d |" %(i, value))
i += 1
i = 0
print("^ Position ^ Argument ^")
for value in self.ArgTable:
print("| %5d | %3d |" %(i, value))
i += 1
i = 0
print("^ Position ^ String ^")
for value in self.StringTable:
print("| %5d | %s |" %(i, value))
i += 1
i = 0
print("^ Position ^ Niveau ^ Token ^ Token ID ^^ Token Type (Card) ^^^ Result ^")
print("^ ^^ (entrée) ^ Valeur ^ Quoi ^ Valeur ^ Card ^ Type ^ ^")
for token in self.TokenTable:
tokenId = token // 8
tokenTypeValue = token & 7
result.write_String("-")
if tokenTypeValue == 0:
tokenCard = 'BEGIN_TOKEN'
tokenType = 'STRUCT_BEGIN'
result.write_Type(TType.STRUCT_BEGIN)
if lvl <= 1:
print("| |||||||")
lvl += 1
elif tokenTypeValue == 1:
tokenCard = 'END_TOKEN'
tokenType = 'STRUCT_END'
result.write_Type(TType.STRUCT_END)
extend = False
extend64 = False
elif tokenTypeValue == 2:
tokenCard = 'SINT_TOKEN'
if extend:
tokenType = 'SINT64'
result.write_Type(TType.SINT64)
result.write_i32_1(self.ArgTable[posArg])
result.write_i32_2(self.ArgTable[posArg+1])
if extend64:
result.write_ex32_2(self.ArgTable[posArg+2]);
posArg += 3
else:
posArg += 2
else:
tokenType = 'SINT32'
result.write_Type(TType.SINT32)
result.write_i32_1(self.ArgTable[posArg])
posArg += 1
extend = False
extend64 = False
elif tokenTypeValue == 3:
tokenCard = 'UINT_TOKEN'
if extend:
tokenType = 'UINT64'
result.write_Type(TType.UINT64)
result.write_i32_1(self.ArgTable[posArg])
result.write_i32_2(self.ArgTable[posArg+1])
if extend64:
result.write_ex32_2(self.ArgTable[posArg+2]);
posArg += 3
else:
posArg += 2
else:
tokenType = 'UINT32'
result.write_Type(TType.UINT32)
result.write_i32_1(self.ArgTable[posArg])
posArg += 1
extend = False
extend64 = False
elif tokenTypeValue == 4:
tokenCard = 'FLOAT_TOKEN'
if extend:
tokenType = 'FLOAT64'
result.write_Type(TType.FLOAT64)
result.write_i32_1(self.ArgTable[posArg])
result.write_i32_2(self.ArgTable[posArg+1])
if extend64:
result.write_ex32_2(self.ArgTable[posArg+2]);
posArg += 3
else:
posArg += 2
else:
tokenType = 'FLOAT32'
result.write_Type(TType.FLOAT32)
result.write_i32_1(self.ArgTable[posArg])
posArg += 1
extend = False
extend64 = False
elif tokenTypeValue == 5:
tokenCard = 'STRING_TOKEN'
if extend:
tokenType = 'EXTEND_TYPE'
result.write_Type(TType.EXTEND_TYPE)
result.write_i32_1(self.ArgTable[posArg])
result.write_i32_2(self.ArgTable[posArg+1])
if extend64:
result.write_ex32_2(self.ArgTable[posArg+2]);
posArg += 3
else:
posArg += 2
else:
tokenType = 'STRING'
result.write_Type(TType.STRING)
result.write_i32_1(self.ArgTable[posArg])
tmp = result.read_i32_1()
result.write_String(self.StringTable[tmp])
posArg += 1
extend = False
extend64 = False
elif tokenType == 6:
tokenCard = 'FLAG_TOKEN'
tokenType = 'FLAG'
result.write_Type(TType.FLAG)
extend = False
extend64 = False
elif tokenTypeValue == 7:
if extend:
extend64 = True
tokenCard = 'EXTEND_TOKEN'
result.write_Type(TType.EXTEND_TYPE)
tokenType = ''
extend = True
# print("token %5d => %3d id:%3d [%s] type:%d [%s]" %(i, token, tokenId, self.StringTable[tokenId], tokenType, tokenCard))
print("| %5d | %3d | %3d | %3d | %s | %d | %s | %s | %s |" %(i, lvl, token, tokenId, self.StringTable[tokenId], tokenTypeValue, tokenCard , tokenType, result))
if tokenTypeValue == 1:
lvl -= 1
i += 1
def addString(self, name): # persistent_data.cpp:100 uint16 CPersistentDataRecord::addString(const string& name)
for i in range(0, len(self.StringTable)):
if self.StringTable[i] == name:
return i
self.StringTable.append(name)
return len(self.StringTable) - 1
def CProductDescriptionForClient_apply(self): # persistent_data_template.h:459 # void PERSISTENT_CLASS::apply(CPersistentDataRecord &pdr _PERSISTENT_APPLY_ARGS)
__Tok__MapKey = self.addString("__Key__")
__Tok__MapVal = self.addString("__Val__")
__Tok_Files = self.addString("_Files")
__Tok_Categories = self.addString("_Categories")
self.log.debug("MapKey:%d, MapVal:%d, Files:%d, Categories:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files, __Tok_Categories))
while not self.isEndOfStruct():
nextToken = self.peekNextToken()
self.log.debug("nextToken:%d" % (nextToken))
if nextToken == __Tok_Files:
self.popStructBegin(__Tok_Files)
self.CBNPFileSet_apply()
self.popStructEnd(__Tok_Files)
continue
elif nextToken == __Tok_Categories:
self.popStructBegin(__Tok_Categories)
# (_Categories).apply(pdr);
self.CBNPCategorySet_apply()
self.popStructEnd(__Tok_Categories)
continue
self.log.error("TODO")
sys.exit(2)
def CBNPFileSet_apply(self):
__Tok__MapKey = self.addString("__Key__")
__Tok__MapVal = self.addString("__Val__")
__Tok_Files = self.addString("_Files")
self.log.debug("MapKey:%d, MapVal:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files))
while not self.isEndOfStruct():
nextToken = self.peekNextToken()
self.log.debug("nextToken:%d" % (nextToken))
if nextToken == __Tok_Files:
self.popStructBegin(__Tok_Files)
self.CBNPFile.append(CBNPFile())
self.CBNPFile_apply(self.CBNPFile[-1])
self.popStructEnd(__Tok_Files)
continue
self.log.error("TODO")
sys.exit(2)
def CBNPFile_apply(self, _CBNPFile):
__Tok__MapKey = self.addString("__Key__")
__Tok__MapVal = self.addString("__Val__")
__Tok_FileName = self.addString("_FileName")
__Tok_Versions = self.addString("_Versions")
_FileName = None
self.log.debug("MapKey:%d, MapVal:%d, Filename:%d, Versions:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_FileName, __Tok_Versions))
while not self.isEndOfStruct():
nextToken = self.peekNextToken()
self.log.debug("nextToken:%d" % (nextToken))
if nextToken == __Tok_FileName:
_FileName = self.popString(nextToken)
_CBNPFile.FileName = _FileName
self.log.debug("filename: %s" % _FileName)
continue
if nextToken == __Tok_Versions:
self.popStructBegin(__Tok_Versions)
# vectAppend(_Versions).apply(pdr);
_CBNPFile.Versions.append(CBNPFileVersion())
self.CBNPFileVersion_apply(_CBNPFile.Versions[-1])
self.popStructEnd(__Tok_Versions)
continue
stack = []
while True:
if self.isStartOfStruct():
stack.append(self.peekNextToken())
self.popStructBegin(stack)
elif self.isEndOfStruct():
self.popStructEnd(stack[-1])
if len(stack) > 0:
del stack[-1]
else:
self.popNextArg(self.peekNextToken())
if self.isEndOfData() and len(stack) == 0:
break
self.log.debug("CBNPFile: %s" % _CBNPFile)
def CBNPFileVersion_apply(self, _CBNPFileVersion): # persistent_data_template.h:459 # void CBNPFileVersion::apply(CPersistentDataRecord &pdr )
__Tok__MapKey = self.addString("__Key__")
__Tok__MapVal = self.addString("__Val__")
__Tok_VersionNumber = self.addString("_VersionNumber")
__Tok_FileSize = self.addString("_FileSize")
__Tok_7ZFileSize = self.addString("_7ZFileSize")
__Tok_FileTime = self.addString("_FileTime")
__Tok_PatchSize = self.addString("_PatchSize")
__Tok_HashKey = self.addString("_HashKey")
self.log.debug("MapKey:%d, MapVal:%d, VersionNumber:%d, FileSize:%d, 7ZFileSize:%d, FileTime:%d, PatchSize:%d, HashKey:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_VersionNumber, __Tok_FileSize, __Tok_7ZFileSize, __Tok_FileTime, __Tok_PatchSize, __Tok_HashKey))
while not self.isEndOfStruct():
nextToken = self.peekNextToken()
self.log.debug("nextToken:%d" % (nextToken))
if nextToken == __Tok_VersionNumber:
self.log.debug("__Tok_VersionNumber")
_CBNPFileVersion.VersionNumber = self.popUint32(__Tok_VersionNumber)
self.log.debug("VersionNumber: %s" % _CBNPFileVersion.VersionNumber)
continue
elif nextToken == __Tok_FileSize:
self.log.debug("__Tok_FileSize")
_CBNPFileVersion.FileSize = self.popUint32(__Tok_FileSize)
self.log.debug("FileSize: %s" % _CBNPFileVersion.FileSize)
continue
elif nextToken == __Tok_7ZFileSize:
self.log.debug("__Tok_7ZFileSize")
_CBNPFileVersion.v7ZFileSize = self.popUint32(__Tok_7ZFileSize)
self.log.debug("7ZFileSize: %s" % _CBNPFileVersion.v7ZFileSize)
continue
elif nextToken == __Tok_FileTime:
self.log.debug("__Tok_FileTime")
_CBNPFileVersion.FileTime = self.popUint32(__Tok_FileTime)
self.log.debug("FileTime: %s" % _CBNPFileVersion.FileTime)
continue
elif nextToken == __Tok_PatchSize:
self.log.debug("__Tok_PatchSize")
_CBNPFileVersion.PatchSize = self.popUint32(__Tok_PatchSize)
self.log.debug("PatchSize: %s" % _CBNPFileVersion.PatchSize)
continue
elif nextToken == __Tok_HashKey:
self.log.debug("__Tok_HashKey")
_CBNPFileVersion.HashKey.append(self.popUint32(__Tok_HashKey))
self.log.debug("HashKey: %s" % _CBNPFileVersion.HashKey[-1])
continue
# Vidage des autres clefs (inconnues)
stack = []
while True:
if self.isStartOfStruct():
stack.append(self.peekNextToken())
self.popStructBegin(stack)
elif self.isEndOfStruct():
self.popStructEnd(stack[-1])
if len(stack) > 0:
del stack[-1]
else:
self.popNextArg(self.peekNextToken())
if self.isEndOfData() and len(stack) == 0:
break
def CBNPCategorySet_apply(self): # persistent_data_template.h:459 # void CBNPCategorySet::apply(CPersistentDataRecord &pdr )
#__Tok__MapKey = self.addString("__Key__")
#__Tok__MapVal = self.addString("__Val__")
__Tok_Category = self.addString("_Category")
while not self.isEndOfStruct():
nextToken = self.peekNextToken()
self.log.debug("nextToken:%d" % (nextToken))
if nextToken == __Tok_Category:
self.log.debug("__Tok_Category")
self.popStructBegin(__Tok_Category)
self.Categories.append(CBNPCategorySet())
self.CBNPCategory_apply(self.Categories[-1])
self.popStructEnd(__Tok_Category)
continue
# Vidage des autres clefs (inconnues)
stack = []
while True:
if self.isStartOfStruct():
stack.append(self.peekNextToken())
self.popStructBegin(stack)
elif self.isEndOfStruct():
self.popStructEnd(stack[-1])
if len(stack) > 0:
del stack[-1]
else:
self.popNextArg(self.peekNextToken())
if self.isEndOfData() and len(stack) == 0:
break
def CBNPCategory_apply(self, _CBNPCategory): # persistent_data_template.h:459 # void CBNPCategory::apply(CPersistentDataRecord &pdr )
__Tok__MapKey = self.addString("__Key__")
__Tok__MapVal = self.addString("__Val__")
__Tok_Name = self.addString("_Name")
__Tok_IsOptional = self.addString("_IsOptional")
__Tok_UnpackTo = self.addString("_UnpackTo")
__Tok_IsIncremental = self.addString("_IsIncremental")
__Tok_CatRequired = self.addString("_CatRequired")
__Tok_Hidden = self.addString("_Hidden")
__Tok_Files = self.addString("_Files")
self.log.debug("MapKey:%d, MapVal:%d, Name:%d, IsOptional:%d, UnpackTo:%d, IsIncremental:%d, CatRequired:%d, Hidden:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Name, __Tok_IsOptional, __Tok_UnpackTo, __Tok_IsIncremental, __Tok_CatRequired, __Tok_Hidden, __Tok_Files))
while not self.isEndOfStruct():
nextToken = self.peekNextToken()
self.log.debug("nextToken:%d" % (nextToken))
if nextToken == __Tok_Name:
self.log.debug("__Tok_Name")
_CBNPCategory._Name = self.popString(nextToken)
self.log.debug("_Name: %s" % _CBNPCategory._Name)
continue
elif nextToken == __Tok_IsOptional:
self.log.debug("__Tok_IsOptional")
_CBNPCategory._IsOptional = self.popBool(nextToken)
self.log.debug("_IsOptional: %s" % str(_CBNPCategory._IsOptional))
continue
elif nextToken == __Tok_UnpackTo:
self.log.debug("__Tok_UnpackTo")
_CBNPCategory._UnpackTo = self.popString(nextToken)
self.log.debug("_UnpackTo: %s" % str(_CBNPCategory._UnpackTo))
continue
elif nextToken == __Tok_IsIncremental:
self.log.debug("__Tok_IsIncremental")
_CBNPCategory._IsIncremental = self.popBool(nextToken)
self.log.debug("_IsIncremental: %s" % str(_CBNPCategory._IsIncremental))
continue
elif nextToken == __Tok_CatRequired:
self.log.debug("__Tok_CatRequired")
_CBNPCategory._CatRequired = self.popString(nextToken)
self.log.debug("_CatRequired: %s" % str(_CBNPCategory._CatRequired))
continue
elif nextToken == __Tok_Hidden:
self.log.debug("__Tok_Hidden")
_CBNPCategory._Hidden = self.popBool(nextToken)
self.log.debug("_Hidden: %s" % str(_CBNPCategory._Hidden))
continue
elif nextToken == __Tok_Files:
self.log.debug("__Tok_Files")
_CBNPCategory._Files.append(self.popString(nextToken))
self.log.debug("_Files: %s" % str(_CBNPCategory._Files))
continue
# Vidage des autres clefs (inconnues)
stack = []
while True:
if self.isStartOfStruct():
stack.append(self.peekNextToken())
self.popStructBegin(stack)
elif self.isEndOfStruct():
self.popStructEnd(stack[-1])
if len(stack) > 0:
del stack[-1]
else:
self.popNextArg(self.peekNextToken())
if self.isEndOfData() and len(stack) == 0:
break
class CAction:
def __init__(self, slot=INVALID_SLOT, code=0):
self.Code = code
self.PropertyCode = code
self.Slot = slot
self._Priority = 1
self.Timeout = 0
self.GameCycle = 0
def unpack(self, message):
raise RuntimeError
def pack(self, message):
raise RuntimeError
def serialIn(self, msgin):
raise RuntimeError
def serialOut(self, msgout):
raise RuntimeError
def size(self):
raise RuntimeError
def getMaxSizeInBit(self):
raise RuntimeError
def setPriority(self, prio):
raise RuntimeError
def priority(self):
raise RuntimeError
def getValue(self):
raise RuntimeError
def setValue(self, value):
raise RuntimeError
def isContinuous(self):
raise RuntimeError
def reset(self):
raise RuntimeError
def __str__(self):
return "[%d,%d]" % (self.Code , self.Slot)
class CActionPosition(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionSync(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionDisconnection(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionAssociation(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionDummy(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionLogin(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionTargetSlot(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionGeneric(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
self._Message = None
def unpack(self, message):
size = message.readUint32()
self._Message = message.readArrayUint8(size)
class CActionGenericMultiPart(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
self.PartCont = []
self.Number = 0
self.Part = 0
self.NbBlock = 0
def unpack(self, message):
self.Number = message.readUint8()
self.Part = message.readUint16()
self.NbBlock = message.readUint16()
size = message.readUint32()
self.PartCont = message.readArrayUint8(size)
class CActionSint64(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionFactory:
def __init__(self):
self.RegisteredAction = []
def create(self, slot, code):
if code == TActionCode.ACTION_POSITION_CODE:
return CActionPosition(slot, code)
elif code == TActionCode.ACTION_GENERIC_CODE:
return CActionGeneric(slot, code)
elif code == TActionCode.ACTION_GENERIC_MULTI_PART_CODE:
return CActionGenericMultiPart(slot, code)
elif code == TActionCode.ACTION_SINT64:
return CActionSint64(slot, code)
elif code == TActionCode.ACTION_SYNC_CODE:
return CActionSync(slot, code)
elif code == TActionCode.ACTION_DISCONNECTION_CODE:
return CActionDisconnection(slot, code)
elif code == TActionCode.ACTION_ASSOCIATION_CODE:
return CActionAssociation(slot, code)
elif code == TActionCode.ACTION_LOGIN_CODE:
return CActionLogin(slot, code)
elif code == TActionCode.ACTION_TARGET_SLOT_CODE:
return CActionTargetSlot(slot, code)
elif code == TActionCode.ACTION_DUMMY_CODE:
return CActionDummy(slot, code)
else:
log = logging.getLogger('myLogger')
log.warning('create() try to create an unknown action (%u)' % code)
raise RuntimeError
def unpack(self, msgin):
'''
khanat-opennel-code/code/ryzom/common/src/game_share/action_factory.cpp : CAction *CActionFactory::unpack (NLMISC::CBitMemStream &message, NLMISC::TGameCycle /* currentCycle */ )
'''
actions = []
if msgin.needRead() >= 8:
shortcode = msgin.readBool()
if shortcode:
code = msgin.readSerial(2)
else:
code = msgin.readUint8()
action = self.create(INVALID_SLOT, code)
if action:
try:
action.unpack (msgin);
except RuntimeError:
log = logging.getLogger('myLogger')
log.warning('Missing code to unpack (code :%u)' % code)
raise RuntimeError
actions.append(action)
else:
log = logging.getLogger('myLogger')
log.warning('Unpacking an action with unknown code, skip it (%u)' % code)
return actions
class CImpulseDecoder:
'''
see : khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp
'''
def __init__(self):
self.reset()
self._CActionFactory = CActionFactory()
def decode(self, msgin, receivedPacket, receivedAck, nextSentPacket):
actions = []
for level in range(0, 3):
if level == 0:
lAck = self._LastAck0
channel = 0
elif level == 1:
lAck = self._LastAck1
channel = receivedPacket & 1
elif level == 2:
lAck = self._LastAck2
channel = receivedPacket & 3
keep = True
checkOnce = False
num = 0
# lastAck = lAck[channel]
while True:
next = msgin.readBool()
if not next:
break
if not checkOnce:
checkOnce = True
keep = receivedAck >= lAck[channel]
if keep:
lAck[channel] = nextSentPacket
num += 1
action = self._CActionFactory.unpack(msgin)
if keep:
actions.append(action)
else:
pass
return actions
def reset(self):
self._LastAck0 = [-1]
self._LastAck1 = [-1, -1]
self._LastAck2 = [-1, -1, -1, -1]
class ClientNetworkConnection:
'''
Partie client de la gestion de la communication reseau avec le serveur:
client :
code/ryzom/client/src/network_connection.cpp
server :
khanat-opennel-code/code/ryzom/server/src/frontend_service/fe_receive_sub.cpp # void CFeReceiveSub::handleReceivedMsg( CClientHost *clienthost )
'''
def __init__(self,
khanat_host,
khanat_port_frontend,
LanguageCode="fr",
checkMessageNumber = True):
self.log = logging.getLogger('myLogger')
self._CurrentSendNumber = 0
self.LanguageCode = LanguageCode
self._QuitId = 0
self._ConnectionState = TConnectionState.NotInitialised
self.UserAddr, self.UserKey, self.UserId = None, None, None
self.frontend = (khanat_host, khanat_port_frontend)
self._sock = None
self._CurrentReceivedNumber = 0
self._SystemMode = 0
self._LastReceivedAck = 0
self._LastReceivedNumber = 0
self._LastAckInLongAck = 0
self._MsgXmlMD5 = None
self._DatabaseXmlMD5 = None
self.msgXml = None
self.databaseXml = None
self._Synchronize = 0
self._LatestSync = 0
self._CurrentServerTick = 0
self._MsPerTick = 0
self._LCT = 100
self._UpdateTime = 0
#self._UpdateTicks = 0
self._ReceivedSync = False
self._LastReceivedTime = 0
self._LastReceivedPacketInBothModes = 0
self._TotalMessages = 0
self._TotalLostPackets = 0
self.checkMessageNumber = checkMessageNumber
self._LastAckBit = 0
self._AckBitMask = 0
self._LongAckBitField = CBitSet()
self._LatestSyncTime = 0
self._ImpulseDecoder = CImpulseDecoder()
self._LongAckBitField.resize(1024)
self._LatestProbeTime = 0
self._LatestProbe = 0
self._LatestProbes = []
self._LatestQuitTime = 0
self._ReceivedAckQuit = False
def signal_exit(self, sig, frame):
self.log.warning("Receive signal to quit program")
self.sendSystemQuit()
sys.exit(0)
def connect(self):
signal.signal(signal.SIGINT, self.signal_exit)
signal.signal(signal.SIGTERM, self.signal_exit)
try:
self._sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
except:
self.log.error("Impossible to connect on khanat")
return False
self._ConnectionState = TConnectionState.Login
self._LatestSyncTime = int(time.clock_gettime(1)*1000)
return True
def cookiesInit(self, UserAddr, UserKey, UserId):
self.UserAddr = UserAddr
self.UserKey = UserKey
self.UserId = UserId
def reset(self):
self._CurrentSendNumber += 0
def buildSystemHeader(self, msgout): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout)
msgout.pushSint32(self._CurrentSendNumber)
msgout.pushBool(True) # systemMode
def sendSystemLogin(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemLogin()
if self._sock is None:
raise ValueError
msgout = BitStream()
self.buildSystemHeader(msgout)
msgout.pushUint8(CLFECOMMON.SYSTEM_LOGIN_CODE)
msgout.pushUint32(self.UserAddr)
msgout.pushUint32(self.UserKey)
msgout.pushUint32(self.UserId)
msgout.pushString(self.LanguageCode)
self._sock.sendto(msgout.toBytes(), self.frontend)
self._CurrentSendNumber += 1
self._ConnectionState = TConnectionState.Login
def sendSystemQuit(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemQuit()
# Disconnect
if self._sock is None:
raise ValueError
self._QuitId += 1
msgout = BitStream()
self.buildSystemHeader(msgout)
msgout.pushUint8(CLFECOMMON.SYSTEM_QUIT_CODE)
msgout.pushSint32(self._QuitId) # _QuitId
self._sock.sendto(msgout.toBytes(), self.frontend)
self._ConnectionState = TConnectionState.Quit
def sendSystemAckSync(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemAckSync()
self.log.debug("sendSystemAckSync")
msgout = BitStream()
self.buildSystemHeader(msgout)
msgout.pushUint8(CLFECOMMON.SYSTEM_ACK_SYNC_CODE)
msgout.pushSint32(self._LastReceivedNumber)
msgout.pushSint32(self._LastAckInLongAck)
self._LongAckBitField.writeSerial(msgout) # Signale le nombre de packet perdu
msgout.pushSint32(self._LatestSync)
self._sock.sendto(msgout.toBytes(), self.frontend)
self._LatestSyncTime = self._UpdateTime
def sendSystemAckProbe(self):
self.log.debug("sendSystemAckProbe")
msgout = BitStream()
self.buildSystemHeader(msgout)
msgout.pushUint8(CLFECOMMON.SYSTEM_ACK_PROBE_CODE)
msgout.pushSint32(len(self._LatestProbes))
for data in self._LatestProbes:
msgout.pushSint32(data)
self._LatestProbes = []
self._sock.sendto(msgout.toBytes(), self.frontend)
def sendSystemDisconnection(self):
if self._sock is None:
raise ValueError
msgout = BitStream()
self.buildSystemHeader(msgout)
msgout.pushUint8(CLFECOMMON.SYSTEM_DISCONNECTION_CODE)
self._sock.sendto(msgout.toBytes(), self.frontend)
def readDelta(self, msg):
propertyCount = msg.readUint16()
self.log.debug("propertyCount:%d" % propertyCount)
for _ in range(0, propertyCount):
pass
def impulseCallBack(self, data):
# code/ryzom/common/src/game_share/generic_xml_msg_mngr.h : CNode *select(NLMISC::CBitMemStream &strm)
msg = BitStream()
msg.fromBytes(data)
serverTick = msg.readUint32()
self.log.debug("serverTick:%d" % serverTick)
#self.readDelta(msg)
def impulseDecode(self, msgin):
actions = self._ImpulseDecoder.decode(msgin, self._CurrentReceivedNumber, self._LastReceivedAck, self._CurrentSendNumber )
self.log.debug(str(actions))
def buildStream(self, buffersize=65536):
# khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::buildStream( CBitMemStream &msgin )
data, addr = self._sock.recvfrom(buffersize)
return data, addr
def decodeHeader(self, msg):
self._TotalMessages += 1
self._LastReceivedTime = self._UpdateTime
self._CurrentReceivedNumber = msg.readSint32()
self._SystemMode = msg.readBool()
if self.checkMessageNumber and self._CurrentReceivedNumber > self._LastReceivedPacketInBothModes:
self._TotalLostPackets += self._CurrentReceivedNumber - self._LastReceivedPacketInBothModes - 1
self._LastReceivedPacketInBothModes = self._CurrentReceivedNumber
else:
self._LastReceivedPacketInBothModes = self._CurrentReceivedNumber - 1
if not self._SystemMode:
self.log.debug("Normal Mode")
self._LastReceivedAck = msg.readSint32();
else:
self.log.debug("System Mode")
if self._CurrentReceivedNumber > self._LastReceivedNumber+1:
self.log.debug("lost messages server->client [%d; %d]" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber - 1))
elif self._CurrentReceivedNumber == self._LastReceivedNumber:
self.log.debug("awaiting packet %d, received packet %d" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber))
return False
elif self._CurrentReceivedNumber < self._LastReceivedNumber:
self.log.debug("received an old message, awaiting packet %d, received packet %d" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber))
return false
ackBool = ( not self._SystemMode ) and (self._ConnectionState == TConnectionState.Connected or self._ConnectionState == TConnectionState.Synchronize)
if ackBool:
ackBit = 1
else:
ackBit = 0
if self._CurrentReceivedNumber - self._LastReceivedNumber < 32:
self._AckBitMask <<= self._CurrentReceivedNumber - self._LastReceivedNumber;
elif (self_CurrentReceivedNumber - self_LastReceivedNumber) == 32 and self._LastAckBit != 0:
self._AckBitMask = 0x80000000
else:
self._AckBitMask = 0x00000000
self._LastAckBit = ackBit;
for i in range(self._LastReceivedNumber+1, self._CurrentReceivedNumber+1):
self._LongAckBitField.clearBit(i & 511)
self._LongAckBitField.set(self._CurrentReceivedNumber & 511, ackBool)
if self._LastAckInLongAck <= (self._CurrentReceivedNumber-512):
self._LastAckInLongAck = self._CurrentReceivedNumber-511;
self._LastReceivedNumber = self._CurrentReceivedNumber
self.log.debug("_CurrentReceivedNumber:%d, _LastReceivedNumber:%d, ackBit:%d, _AckBitMask:%d _LongAckBitField:%s" % (self._CurrentReceivedNumber, self._LastReceivedNumber, ackBit, self._AckBitMask, self._LongAckBitField))
return True
def receiveSystemProbe(self, msg):
self._LatestProbeTime = self._UpdateTime
self._LatestProbe = msg.readSint32()
self.log.debug("LatestProbe: %d" % self._LatestProbe)
self._LatestProbes.append(self._LatestProbe)
def receiveSystemStalled(self, msg):
self.log.debug("received STALLED")
def receiveSystemSync(self, msg):
self._LatestSyncTime = self._UpdateTime
self._Synchronize = msg.readUint32()
stime = msg.readSint64()
self._LatestSync = msg.readUint32()
self.log.debug("%d %d %d" %(self._Synchronize, stime, self._LatestSync))
# khanat-opennel-code/code/ryzom/client/src/network_connection.cpp : void CNetworkConnection::receiveSystemSync(CBitMemStream &msgin)
MsgData = msg.readArrayUint8(16)
DatabaseData = msg.readArrayUint8(16)
self.log.debug("MsgData:" + str(MsgData))
self.log.debug("DatabaseData:" + str(DatabaseData))
md5Msg = bytes(MsgData)
md5Database = bytes(DatabaseData)
if md5Msg == self._MsgXmlMD5:
self.log.info("Check MD5 msg.xml : OK")
else:
self.log.error("Check MD5 msg.xml : KO")
if md5Database == self._DatabaseXmlMD5:
self.log.info("Check MD5 database.xml : OK")
else:
self.log.error("Check MD5 database.xml : KO")
self._MsPerTick = 100
self._CurrentServerTick = self._Synchronize + self._CurrentReceivedNumber + 2
self._CurrentClientTick = self._CurrentServerTick - ( self._LCT + self._MsPerTick ) / self._MsPerTick
self._CurrentClientTime = self._UpdateTime - (self._LCT + self._MsPerTick)
self.sendSystemAckSync()
def receiveNormalMessage(self, msgin):
self.log.debug("received normal message Packet (%d)" % (msgin.needRead()))
self.impulseDecode(msgin)
def receiveSystemAckQuit(self, msgin):
self.log.debug("received ACK_QUIT")
self._ReceivedAckQuit = True
def disconnect(self):
self.log.info("Disconnect")
self.sendSystemDisconnection()
self._sock.close()
selc._sock = None
self._ConnectionState = TConnectionState.Disconnect
def stateLogin(self, msgin):
self.decodeHeader(msgin)
if self._SystemMode:
message = msgin.readUint8()
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead()))
if message == CLFECOMMON.SYSTEM_SYNC_CODE:
self._ConnectionState = TConnectionState.Synchronize
self.log.debug("Login->synchronize")
self.receiveSystemSync(msgin)
return True
elif message == CLFECOMMON.SYSTEM_STALLED_CODE:
self.log.debug("received STALLED")
self._ConnectionState = TConnectionState.Stalled
self.receiveSystemStalled(msgin)
return True
elif message == CLFECOMMON.SYSTEM_PROBE_CODE:
self.log.debug("Login->probe")
self._ConnectionState = TConnectionState.Probe
self.receiveSystemProbe(msgin)
return True
elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE:
self.disconnect()
self.log.warning("BACK-END DOWN")
return False
else:
self.log.warning("CNET: received system %d in state Login" % message)
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData()))
else:
self.log.warning("CNET: received normal in state Login")
return False
def stateSynchronize(self, msgin):
self.decodeHeader(msgin)
if self._SystemMode:
message = msgin.readUint8()
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead()))
if message == CLFECOMMON.SYSTEM_PROBE_CODE:
self.log.debug("synchronize->probe")
self._ConnectionState = TConnectionState.Probe
self.receiveSystemProbe(msgin)
return True
elif message == CLFECOMMON.SYSTEM_STALLED_CODE:
self.log.debug("received STALLED")
self._ConnectionState = TConnectionState.Stalled
self.receiveSystemStalled(msgin)
return True
elif message == CLFECOMMON.SYSTEM_SYNC_CODE:
self.log.debug("synchronize->synchronize")
self.receiveSystemSync(msgin)
elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE:
self.disconnect()
self.log.warning("BACK-END DOWN")
return False
else:
self.log.warning("CNET: received system %d in state Synchronize" % message)
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData()))
else:
self._ConnectionState = TConnectionState.Connected
self.log.warning("CNET: synchronize->connected")
# _Changes.push_back(CChange(0, ConnectionReady));
self.receiveNormalMessage(msgin);
return True
if self._UpdateTime - self._LatestSyncTime > 300:
self.sendSystemAckSync();
return False
def stateConnected(self, msgin):
self.decodeHeader(msgin)
if self._SystemMode:
message = msgin.readUint8()
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead()))
if message == CLFECOMMON.SYSTEM_PROBE_CODE:
self.log.debug("Connected->probe")
self._ConnectionState = TConnectionState.Probe
self.receiveSystemProbe(msgin)
return True
elif message == CLFECOMMON.SYSTEM_SYNC_CODE:
self.log.debug("Connected->synchronize")
self.receiveSystemSync(msgin)
return True
elif message == CLFECOMMON.SYSTEM_STALLED_CODE:
self.log.debug("received STALLED")
self._ConnectionState = TConnectionState.Stalled
self.receiveSystemStalled(msgin)
return True
elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE:
self.disconnect()
self.log.warning("BACK-END DOWN")
return False
else:
self.log.warning("CNET: received system %d in state Connected" % message)
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData()))
else:
self.receiveNormalMessage(msgin);
return True
return False
def stateProbe(self, msgin):
self.decodeHeader(msgin)
if self._SystemMode:
message = msgin.readUint8()
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead()))
if message == CLFECOMMON.SYSTEM_SYNC_CODE:
self.log.debug("probe->synchronize")
self._ConnectionState = TConnectionState.Synchronize
self.receiveSystemSync(msgin)
return True
elif message == CLFECOMMON.SYSTEM_STALLED_CODE:
self.log.debug("probe->stalled")
self._ConnectionState = TConnectionState.Stalled
self.receiveSystemStalled(msgin)
return True
elif message == CLFECOMMON.SYSTEM_PROBE_CODE:
self.receiveSystemProbe(msgin)
elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE:
self.disconnect()
self.log.warning("BACK-END DOWN")
return False
else:
self.log.warning("CNET: received system %d in state Probe" % message)
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData()))
else:
self.log.warning("received normal in state Probe")
if (len(self._LatestProbes) > 0) or (self._UpdateTime - self._LatestProbeTime > 300):
self.sendSystemAckProbe()
self._LatestProbeTime = self._UpdateTime
return False
def stateStalled(self, msgin):
self.decodeHeader(msgin)
if self._SystemMode:
message = msgin.readUint8()
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead()))
if message == CLFECOMMON.SYSTEM_SYNC_CODE:
self.log.debug("stalled->synchronize")
self._ConnectionState = TConnectionState.Synchronize
self.receiveSystemSync(msgin)
return True
elif message == CLFECOMMON.SYSTEM_PROBE_CODE:
self.log.debug("stalled->probe")
self._ConnectionState = TConnectionState.Probe
self.receiveSystemProbe(msgin)
elif message == CLFECOMMON.SYSTEM_STALLED_CODE:
self.receiveSystemStalled(msgin)
elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE:
self.disconnect()
self.log.warning("BACK-END DOWN")
return False
else:
self.log.warning("CNET: received system %d in state Stalled" % message)
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData()))
else:
self.log.warning("received normal in state Stalled")
return False
def stateQuit(self, msgin):
self.decodeHeader(msgin)
if self._SystemMode:
message = msgin.readUint8()
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead()))
if message == CLFECOMMON.SYSTEM_SYNC_CODE:
self.log.debug("quit->synchronize")
self._ConnectionState = TConnectionState.Synchronize
self.receiveSystemSync(msgin)
return True
elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE:
self.disconnect()
self.log.warning("BACK-END DOWN")
return False
elif message == CLFECOMMON.SYSTEM_ACK_QUIT_CODE:
self.receiveSystemAckQuit(msgin)
else:
self.log.warning("CNET: received system %d in state Quit" % message)
self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData()))
else:
self.log.warning("received normal in state Quit")
if not self._ReceivedAckQuit and (self._UpdateTime - self._LatestQuitTime > 100):
self.sendSystemQuit()
self._LatestQuitTime = self._UpdateTime
return False
def update(self):
# khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::update()
self._UpdateTime = int(time.clock_gettime(1)*1000)
self._ReceivedSync = False
if not self._sock:
return False
# TODO - REMOVE this counter (just to stop loop)
counterLoop = 0
stateBroke = True
while stateBroke:
buffer, addr = self.buildStream()
self.log.debug("received message: %s" % buffer)
msgin = BitStream()
msgin.fromBytes(buffer)
if self._ConnectionState == TConnectionState.Login:
self.log.debug("state:Login")
stateBroke = self.stateLogin(msgin)
elif self._ConnectionState == TConnectionState.Synchronize:
self.log.debug("state:Synchronize")
stateBroke = self.stateSynchronize(msgin)
elif self._ConnectionState == TConnectionState.Connected:
self.log.debug("state:Connected")
stateBroke = self.stateConnected(msgin)
elif self._ConnectionState == TConnectionState.Probe:
self.log.debug("state:Probe")
stateBroke = self.stateProbe(msgin)
elif self._ConnectionState == TConnectionState.Stalled:
self.log.debug("state:Stalled")
stateBroke = self.stateStalled(msgin)
elif self._ConnectionState == TConnectionState.Quit:
self.log.debug("state:Quit")
stateBroke = self.stateQuit(msgin)
else:
stateBroke = False
counterLoop += 1
if counterLoop > 10:
break
def EmulateFirst(self, msgRawXml, databaseRawXml):
self.msgXml = ET.fromstring(msgRawXml)
#ET.dump(msgXml)
self.databaseXml = ET.fromstring(databaseRawXml)
#ET.dump(databaseXml)
self._MsgXmlMD5 = getTextMD5(msgRawXml)
self._DatabaseXmlMD5 = getTextMD5(databaseRawXml)
self.connect()
self.log.info("Client Login")
self.sendSystemLogin()
self.log.info("Receive Message")
for i in range(0, 2): # while True:
self.log.debug('loop %d' % i)
self.update()
self.log.info("Client Quit")
self.sendSystemQuit()
class ClientKhanat:
def __init__(self,
khanat_host,
khanat_port_login = 40916,
khanat_port_frontend = 47851,
login="tester",
password="tester",
clientApp="Lirria",
LanguageCode="fr",
url="/login/r2_login.php",
suffix = None,
download_patch = False,
show_patch_detail=False,
size_buffer_file=1024):
self.log = logging.getLogger('myLogger')
if suffix is None:
suffix = str(random.randrange(1, 9999))
self.log.debug("suffix : %s" % suffix)
self.download_patch = download_patch
self.show_patch_detail = show_patch_detail
self.khanat_host = khanat_host
self.khanat_port_login = khanat_port_login
self.khanat_port_frontend = khanat_port_frontend
self.login = login + suffix
self.password = password
self.clientApp = clientApp
self.LanguageCode = LanguageCode
self.url = url
self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat, self.r2serverversion, self.r2backuppatchurl, self.r2patchurl = None, None, None, None, None, None, None, None
self.tempdir = tempfile.TemporaryDirectory(".khanat")
self.log.debug("Temporary directory:%s" % self.tempdir)
self.khanat_idx = CPersistentDataRecord(self.log)
self.UserAddr, self.UserKey, self.UserId = None, None, None
self.clientNetworkConnection = ClientNetworkConnection(self.khanat_host, self.khanat_port_frontend)
self.size_buffer_file = size_buffer_file
self.cFileContainer = CFileContainer()
def createAccount(self):
conn = http.client.HTTPConnection(host=self.khanat_host, port=self.khanat_port_login)
cmd = "/ams/index.php?page=register"
headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' : 'en-US',
'Connection': 'keep-alive',
'DNT': '1',
'Cookie': 'PHPSESSID=lsoumn9f0ljgm3vo3hgjdead03',
'Host': self.khanat_host+':'+ str(self.khanat_port_login),
'Referer': 'http://' + self.khanat_host+':'+ str(self.khanat_port_login) + '/ams/index.php?page=register',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20100101 Firefox/6.0',
'Content-Type': 'application/x-www-form-urlencoded'}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
params = urllib.parse.urlencode({'Username': self.login, 'Password': self.password, 'ConfirmPass': self.password, 'Email': self.login+'@khaganat.net', 'TaC': 'on', 'function': 'add_user'})
conn.request("POST", cmd, params, headers)
response = conn.getresponse()
if ( int(response.status) == 302 ):
conn.close()
self.log.info("Account : %s" % self.login)
return
elif ( int(response.status) != 200 ):
self.log.error("Impossible to create account (return code:" + str(response.status) + ")")
sys.exit(2)
ret = response.read()
conn.close()
ret2 = ret.decode()
try:
state, comment = ret2.split(":", 2)
except:
state = 1
comment = ""
if int(state) != 1:
self.log.error("Impossible to create account (state:" + state + ", comment:" + comment.strip() + ")")
sys.exit(2)
errordetected = False
for line in ret2.split('\n'):
m = re.search("(<strong>(?P<type>.*) Error</strong> )(?P<comment>[^.]+)", line)
if m:
if m.group('comment') == 'Username ' + self.login + ' is in use':
continue
if m.group('comment') == 'Email is in use':
continue
self.log.error('Impossible to create account: field:%s (%s)' % (m.group('type'), m.group('comment')))
errordetected = True
if errordetected:
sys.exit(2)
self.log.info("Reuse account : %s" % self.login)
def connectR2(self):
conn = http.client.HTTPConnection(host=self.khanat_host, port=self.khanat_port_login)
cmd = self.url + "?cmd=ask&cp=2&login=" + self.login + "&lg=" + self.LanguageCode
conn.request("GET", cmd)
response = conn.getresponse()
if ( int(response.status) != 200 ):
self.log.error("Impossible to get salt (return code:" + str(response.status) + ")")
sys.exit(2)
ret = response.read()
conn.close()
try:
state, salt = ret.decode().split(":", 1)
except UnicodeDecodeError:
try:
state, salt = ret.decode(encoding='cp1252').split(":", 1)
except UnicodeDecodeError:
self.log.error("Impossible to read output login")
sys.exit(2)
if int(state) != 1:
self.log.error("Impossible to get salt (state:" + state + ")")
cryptedPassword = crypt.crypt(self.password, salt)
conn = http.client.HTTPConnection(host=self.khanat_host, port=self.khanat_port_login)
cmd = self.url + "?cmd=login&login=" + self.login + "&password=" + cryptedPassword + "&clientApplication=" + self.clientApp + "&cp=2" + "&lg=" + self.LanguageCode
conn.request("GET", cmd)
response = conn.getresponse()
self.log.debug("%s %s" %(response.status, response.reason))
ret = response.read()
self.log.debug(ret)
try:
line = ret.decode().split('\n')
except UnicodeDecodeError:
try:
line = ret.decode(encoding='cp1252').split('\n')
except UnicodeDecodeError:
self.log.error("Impossible to read output login")
sys.exit(2)
self.log.debug(line[0])
self.log.debug("line 0 '%s'" % line[0])
self.log.debug("line 1 '%s'" % line[1])
try:
state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat = line[0].split("#", 6)
except:
try:
state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp = line[0].split("#", 5)
self.stat = 0
except:
state, error = line[0].split(":", 1)
if int(state) != 1:
self.log.error(error)
sys.exit(2)
self.r2serverversion, self.r2backuppatchurl, self.r2patchurl = line[1].split("#")
self.log.debug("%s %s %s %s %s %s %s %s %s" % (state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat, self.r2serverversion, self.r2backuppatchurl, self.r2patchurl))
self.UserAddr, self.UserKey, self.UserId = [ int(x, 16) for x in self.cookie.split('|') ]
conn.close()
self.log.info("Login Ok")
self.clientNetworkConnection.cookiesInit(self.UserAddr, self.UserKey, self.UserId)
def downloadFileUrl(self, source, dest):
self.log.info("Download %s (destination:%s)" % (source, dest))
with urllib.request.urlopen(source) as conn :
header = conn.getheaders()
file_size = 0
for key, value in header:
if key == 'Content-Length':
file_size = int(value)
break
self.log.debug("size:%d", file_size)
file_size_dl = 0
block_size = self.size_buffer_file # 1024
with open(dest, 'wb') as fp:
while True:
buffer = conn.read(block_size)
if not buffer:
break
file_size_dl += len(buffer)
fp.write(buffer)
self.log.debug("Download %s %10d [%6.2f%%]" % (source, file_size_dl, file_size_dl * 100. / file_size))
fp.close()
self.log.debug("Downloaded %s (%d)" % (source, file_size))
def getServerFile(self, name, bZipped = False, specifyDestName = None):
srcName = name
if specifyDestName:
dstName = specifyDestName
else:
dstName = os.path.basename(name)
if bZipped:
srcName += ".ngz"
dstName += ".ngz"
self.log.info("Download %s (destination:%s, zip:%d)" % (srcName, dstName, bZipped))
dstName = os.path.join(self.tempdir.name, dstName)
self.downloadFileUrl( 'http://' + self.r2patchurl + '/' + srcName, dstName)
return dstName
def downloadAllPatch(self):
# TODO - check where client search file to download
for file in self.khanat_idx.CBNPFile:
tmp = self.getServerFile("%05d/%s.lzma" % (int(self.r2serverversion), file.FileName), False, "")
with lzma.open(tmp) as fin:
dstName = os.path.join(self.tempdir.name, file.FileName)
with open(dstName, "wb") as fout:
data = fin.read()
fout.write(data)
self.log.info("%s" % dstName)
os.remove(tmp)
# khanat-opennel-code/code/ryzom/client/src/login_patch.cpp # void CCheckThread::run ()
FilesToPatch = []
for file in self.khanat_idx.CBNPFile:
FilesToPatch.append(file)
# Here we got all the files to patch in FilesToPatch and all the versions that must be obtained Now we have to get the optional categories
OptionalCat = []
for category in self.khanat_idx.Categories:
if category._IsOptional:
for file in category._Files:
bAdded = False
for file2 in FilesToPatch:
if file2 == file:
OptionalCat.append(category._Name)
bAdded = True
break
if bAdded:
break
# For all categories that required an optional category if the cat required is present the category that reference it must be present
for category in self.khanat_idx.Categories:
if category._IsOptional and not len(category._CatRequired) == 0:
bFound = False
for cat in OptionalCat:
if category._Name == cat:
bFound = True
break
if bFound:
for cat in OptionalCat:
if category._CatRequired == cat:
OptionalCat.append(category._Name)
break
# Delete categories optional cat that are hidden
for category in self.khanat_idx.Categories:
if category._IsOptional and category._Hidden:
for cat in OptionalCat:
if category._Name == cat:
OptionalCat.remove(category._Name)
break
# Get all extract to category and check files inside the bnp with real files
for category in self.khanat_idx.Categories:
if len(category._UnpackTo) != 0:
for file in category._Files:
# TODO
# readHeader()
pass
def DownloadMinimum(self):
self.log.debug("-" * 80)
for file in self.khanat_idx.CBNPFile:
if file.FileName != "kh_server.bnp":
continue
tmp = self.getServerFile("%05d/%s.lzma" % (int(self.r2serverversion), file.FileName), False, "")
with lzma.open(tmp) as fin:
dstName = os.path.join(self.tempdir.name, file.FileName)
with open(dstName, "wb") as fout:
data = fin.read()
fout.write(data)
self.log.info("%s" % dstName)
os.remove(tmp)
def Emulate(self):
self.createAccount()
self.connectR2()
# download patch
self.ryzomidx = self.getServerFile("%05d/ryzom_%05d.idx" % (int(self.r2serverversion), int(self.r2serverversion)), False, "")
self.khanat_idx.readFromBinFile(self.ryzomidx)
self.khanat_idx.CProductDescriptionForClient_apply()
# Show detail patch
if self.show_patch_detail:
self.khanat_idx.decrypt_token()
self.khanat_idx.show()
# Todo analyze patch and download if necessary or update if incremental - see category
# Download all file in patch - login_patch.cpp:2578 # void CPatchThread::processFile (CPatchManager::SFileToPatch &rFTP)
if self.download_patch:
self.downloadAllPatch()
else:
self.DownloadMinimum()
self.cFileContainer = CFileContainer()
self.cFileContainer.addSearchPath(self.tempdir.name)
msgRawXml = self.cFileContainer.getdata("msg.xml").decode()
databaseRawXml = self.cFileContainer.getdata("database.xml").decode()
self.clientNetworkConnection.EmulateFirst(msgRawXml, databaseRawXml)
def main():
FORMAT = '%(asctime)-15s %(levelname)s %(filename)s:%(lineno)d %(message)s'
logging.basicConfig(format=FORMAT)
log = logging.getLogger('myLogger')
parser = argparse.ArgumentParser()
parser.add_argument("--khanat-host", help="khanat host to auhtenticate", default='localhost')
parser.add_argument("--suffix", help="define suffix")
parser.add_argument("-d", "--debug", help="show debug message", action='store_true')
parser.add_argument("-p", "--download-patch", help="show debug message", action='store_true')
parser.add_argument("-s", "--show-patch-detail", help="show debug message", action='store_true')
parser.add_argument("--size-buffer-file", help="size buffer to download file", type=int, default=1024)
parser.add_argument("--khanat-port-login", help="port http login", type=int, default=40916)
parser.add_argument("--khanat-port-frontend", help="port UDP frontend", type=int, default=47851)
args = parser.parse_args()
if args.debug:
level = logging.getLevelName('DEBUG')
else:
level = logging.getLevelName('INFO')
log.setLevel(level)
client = ClientKhanat(args.khanat_host, khanat_port_login=args.khanat_port_login, khanat_port_frontend=args.khanat_port_frontend, suffix=args.suffix, download_patch=args.download_patch, show_patch_detail=args.show_patch_detail, size_buffer_file=args.size_buffer_file)
client.Emulate()
log.info("End")
if __name__ == "__main__":
#TestBitStream()
#TestCBitSet()
main()