Python [Python] Problème lecture fichier d2p/dlm

Inscrit
1 Mai 2014
Messages
20
Reactions
0
#1
Bonjour voici le synopsis :
j'étais tranquillement en train de coder un bot MITM et je devais donc m'attaquer à la lecture des fichiers d2p pour en extraire toutes leurs précieuses informations. Tout se passa bien quand soudain un problème surgis de nulle part. Tout les headers de mes fichiers dlm récupérés valent 120 et non pas 77!
Voilà ma classe qui me permet de récupérer les .dlm, c'est un copié coller de PakProtocol2.as :
Code:
import os, io, struct

MAP_DIR = "C:/Users/Ascension/Documents/Coding/BotProject/MapsReader/D2MAPS"

class MapFiles:
	"""Create a dictionary as attribute indexes[filePath] = 
	{"o":(fileOffset + dataOffset),	"l":fileLength, "stream":f }
	"""
	def __init__(self):
		self.indexes = self.getMapsFromD2P()

	def getMapsFromD2P(self):
		##Go to the right directory
		os.chdir(MAP_DIR)
		files_list = os.listdir(MAP_DIR)
		properties = {}
		indexes = {}

		for file in files_list:
			if os.path.splitext(file)[-1] == ".d2p" :

				##open D2P file
				f = io.FileIO(file, "r")

				##read header
				vMax = f.read(1)[0]
				vMin = f.read(1)[0]
				if vMax != 2 or vMin!=1:
					return None

				##read parametres at the end of the file
				f.seek(self.getSize(f) - 24)
				unpackedData = struct.unpack("!IIIIII", f.read(24))
				dataOffset = unpackedData[0]
				dataCount = unpackedData[1]
				indexOffset = unpackedData[2]
				indexCount = unpackedData[3]
				propertiesOffset = unpackedData[4]
				propertiesCount = unpackedData[5]

				##read properties
				f.seek(propertiesOffset)
				i = 0
				while i < propertiesCount :
					length = struct.unpack("!h",f.read(2))[0]
					propertyName = f.read(length).decode('utf-8')
					length = struct.unpack("!H",f.read(2))[0]
					propertyValue = f.read(length).decode('utf-8')
					properties[propertyName+file] = propertyValue
					if propertyName == "link":
						#we take care of it
						pass
					i+=1

				##read files
				f.seek(indexOffset)
				i = 0
				while i < indexCount :
					length = struct.unpack('!H', f.read(2))[0]
					filePath = f.read(length).decode('utf-8')
					fileOffset = struct.unpack("!I", f.read(4))[0]
					fileLength = struct.unpack("!I", f.read(4))[0]
					indexes[filePath] = {"o":(fileOffset + dataOffset),
					"l":fileLength,
					"stream":f }
					i+=1
				
			else:
				pass

		##Debug
		print("%d maps have just been extracted"%len(indexes))
		#for x in indexes: print(x)

		return indexes
J'obtiens un dictionnaire de taille 11381 (= nbr de map ?)
Au début je pensais que c'était juste un problème d'offset mal réglé dans la lecture des .d2p, mais quand je cherche la valeurs 77 avec le code ci-dessous :

Code:
class MapInfo(MapFiles):

		def test(self):
			ex1 = self.indexes['3/137253.dlm']
			exMap1 = self.extractData(ex1['l'], ex1['o'], ex1['stream'])
		
		def extractData(self, l, o, stream):
			##init vars
			decryptionKey = bytes("649ae451ca33ec53bbcbcc33becf15f4", "utf-8")
			n = len(decryptionKey)
			f = stream
			f.seek(o)
			
			##Read header
			header = struct.unpack("!"+"b"*100, f.read(100)) #try to find 77 !
			if 77 in header:
				print("TRUE AT %d"%header.index(77))
			if header == 77:
				mapVersion = f.read(1)[0]
			else: 
				print("ERROR")

			##Debug
			print(header)
			print(f.read(l))

mapFiles = MapInfo()
mapFiles.test()
Je remarque parfois que j'ai un octect qui prend la valeur 77 mais décalé de 50 ou 51 places !
J'ai beau revérifier, je ne trouve pas mon erreur. La question est : Est-ce que vous avez bien aussi 11381 maps? Dans ce cas ma première class est juste. Sinon, avez vous une idée d'où l'erreur pourrait venir? :(
 

Labo

Membre Actif
Inscrit
16 Aout 2013
Messages
799
Reactions
15
#2
Je n'ai pas regardé en détail ton code, mais peut-être as-tu fait la même erreur que moi, à savoir oublier de décompresser le fichier zip.
 
Inscrit
1 Mai 2014
Messages
20
Reactions
0
#3
Nope j'ai jamais dézippé quoi que ce soit :o C'est grave??? J'ai juste pris le dossier map dans les sources. Après ce que j'ai me semblait pas du tout aberrant.
 

Labo

Membre Actif
Inscrit
16 Aout 2013
Messages
799
Reactions
15
#4
Cherche un ficher appelé MapsAdapter.as
En le cherchant, je me suis souvenu à quel point leur système d'adapter et de loader est chiant !
Je ne suis plus très au fait, mais en survolant vite, il me semble que l'appel est fait sous la forme
Code:
getAdapter(uri,forcedAdapter)
 
Dernière édition par un modérateur:

Labo

Membre Actif
Inscrit
16 Aout 2013
Messages
799
Reactions
15
#5
Je ne sais pas ce que ça vaut, mais voici un de mes codes (dégueulasse) :
Code:
from API.data.const import *
from API.data.data import *

class MapLoader():
	def __init__(self, fichier='/Applications/Dofus.app/Contents/Data/Dofus.app/Contents/Resources/content/maps/maps0.d2p'):
		self.indexes = dict()
		self.properties = dict()
		self.initStream(fichier)
	
	def load(self, mapId):
		index = self.indexes[str(mapId%10)+'/'+str(mapId)+'.dlm']
		fs = index['stream']
		fs.pos = index['o']
		data = Data(fs.readBytes(index['l']))
		map = Map()
		map.fromRaw(data)
		return map
		
	def initStream(self, chemin):
		file = chemin
		while file:
			with open(file,'rb') as f:
				fs=Data(f.read())
			file = ""
		
			fs.readUnsignedByte() # == 2
			fs.readUnsignedByte() # == 1
		
			fs.pos = len(fs)-24
			dataOffset = fs.readUnsignedInt()
			dataCount = fs.readUnsignedInt()
			indexOffset = fs.readUnsignedInt()
			indexCount = fs.readUnsignedInt()
			propertiesOffset = fs.readUnsignedInt()
			propertiesCount = fs.readUnsignedInt()
			#print(chemin,dataOffset,dataCount,indexOffset,indexCount,propertiesOffset,propertiesCount,len(fs)-24,len(fs))
						
			fs.pos = propertiesOffset
			for i in range(propertiesCount):
				propertyName = fs.readUTF()
				propertyValue = fs.readUTF()
				self.properties[propertyName] = propertyValue
				if propertyName == "link":
					index = chemin[::-1].find("/")
					if index != -1:
						chemin = chemin[0:-index-1]+"/"+propertyValue
					else:
						chemin = propertyValue
					file = chemin

			fs.pos = indexOffset
			for i in range(indexCount):
				filePath = fs.readUTF()
				fileOffset = fs.readInt()
				fileLength = fs.readInt()
				self.indexes[filePath] = {"o":fileOffset + dataOffset, "l":fileLength, "stream":fs}


def getMapIdFromCoord(worldId,x,y):
	worldIdMax = 2 << 12
	mapCoordMax = 2 << 8
	if((x > mapCoordMax) or (y > mapCoordMax) or (worldId > worldIdMax)):
		return -1
	newWorldId = worldId & 4095
	newX = abs(x) & 255
	if(x < 0):
		newX = newX | 256
	newY = abs(y) & 255
	if(y < 0):
		newY = newY | 256
	return newWorldId << 18 | newX << 9 | newY

# def getMapUriFromId(mapId):
# 	return '/Applications/Dofus.app/Contents/Data/Dofus.app/Contents/Resources/content/maps/maps'+str(mapId%10)+'/'+str(mapId)+'.dlm'


class WorldPoint():
	WORLD_ID_MAX = 2 << 12
	MAP_COORDS_MAX = 2 << 8
	def __init__(self):
		pass
	def fromMapId(mapId):
		wp = WorldPoint()
		wp._mapId = mapId
		wp.setFromMapId()
		return wp
	def fromCoords(worldId, x, y):
		wp = WorldPoint()
		wp._worldId = worldId
		wp._x = x
		wp._y = y
		wp.setFromCoords()
		return wp
	def setFromMapId():
		self._worldId = (self._mapId & 1073479680) >> 18
		self._x = self._mapId >> 9 & 511
		self._y = self._mapId & 511
		if((self._x & 256) == 256):
		   self._x = -(self._x & 255)
		if((self._y & 256) == 256):
		   self._y = -(self._y & 255)
	def setFromCoords():
		if (self._x > MAP_COORDS_MAX) or (self._y > MAP_COORDS_MAX) or (self._worldId > WORLD_ID_MAX):
			raise Exception("Coordinates or world identifier out of range.")
		else:
			worldValue = self._worldId & 4095
			xValue = Math.abs(self._x) & 255
			if(self._x < 0):
			   xValue = xValue | 256
			yValue = Math.abs(self._y) & 255
			if(self._y < 0):
			   yValue = yValue | 256
			self._mapId = worldValue << 18 | xValue << 9 | yValue		


# les raw sont des Data
class Map():
	decryptionKey=bytes.fromhex("649ae451ca33ec53bbcbcc33becf15f4")
	def __init__(self):
		self.topArrowCell = []
		self.leftArrowCell = []
		self.bottomArrowCell = []
		self.rightArrowCell = []

	def fromRaw(self,raw):
		
		_oldMvtSystem = 0
		
		header = raw.readByte()
		if header != 77:
			raw.pos = 0
			raw.uncompress()
			header = raw.readByte()
			if header != 77:
				raise Exception('! erreur header ! '+str(header))
		self.mapVersion = raw.readByte()
		self.id = raw.readUnsignedInt()
		if(self.mapVersion >= 7):
			self.encrypted = raw.readBoolean()
			self.encryptionVersion = raw.readByte()
			dataLen = raw.readInt()
			if(self.encrypted):
				encryptedData = list(raw.readBytes(dataLen))
				for i in range(len(encryptedData)):
					encryptedData[i] = encryptedData[i] ^ self.decryptionKey[i % len(self.decryptionKey)]
				raw = Data(bytes(encryptedData))
		
		self.relativeId = raw.readUnsignedInt()
		self.mapType = raw.readByte()
		self.subareaId = raw.readInt()
		self.topNeighbourId = raw.readInt()
		self.bottomNeighbourId = raw.readInt()
		self.leftNeighbourId = raw.readInt()
		self.rightNeighbourId = raw.readInt()
		self.shadowBonusOnEntities = raw.readInt()
		if(self.mapVersion >= 3):
			self.backgroundRed = raw.readByte()
			self.backgroundGreen = raw.readByte()
			self.backgroundBlue = raw.readByte()
			self.backgroundColor = (self.backgroundRed & 255) << 16 | (self.backgroundGreen & 255) << 8 | self.backgroundBlue & 255
		if(self.mapVersion >= 4):
			self.zoomScale = raw.readUnsignedShort() / 100
			self.zoomOffsetX = raw.readShort()
			self.zoomOffsetY = raw.readShort()
			if(self.zoomScale < 1):
				self.zoomScale = 1
				self.zoomOffsetX = self.zoomOffsetY = 0
		self.useLowPassFilter = raw.readByte() == 1
		self.useReverb = raw.readByte() == 1
		if(self.useReverb):
			self.presetId = raw.readInt()
		else:
			self.presetId = -1
		self.backgroundsCount = raw.readByte()
		self.backgroundFixtures = list()
		for i in range(self.backgroundsCount):
			bg = Fixture()
			bg.fromRaw(raw)
			self.backgroundFixtures.append(bg)
		self.foregroundsCount = raw.readByte()
		self.backgroundFixtures = list()
		for i in range(self.backgroundsCount):
			fg = Fixture()
			fg.fromRaw(raw)
			self.backgroundFixtures.append(fg)
		self.cellsCount = AtouinConstants.MAP_CELLS_COUNT
		raw.readInt()
		self.groundCRC = raw.readInt()
		self.layersCount = raw.readByte()
		self.layers = list()
		for i in range(self.layersCount):
			la = Layer()
			la.fromRaw(raw,self.mapVersion)
			self.layers.append(la)
		self.cells = list()
		for i in range(self.cellsCount):
			cd = CellData(self, i)
			cd.fromRaw(raw)
			if not _oldMvtSystem:
				_oldMvtSystem = cd.moveZone
			if(cd.moveZone != _oldMvtSystem):
				self.isUsingNewMovementSystem = True
			self.cells.append(cd)
		self._parsed = True
				
				
class Fixture():
	def __init__(self, map):
		pass
	def fromRaw(self, raw):
		self.fixtureId = raw.readInt()
		self.offset = Point()
		self.offset.x = raw.readShort()
		self.offset.y = raw.readShort()
		self.rotation = raw.readShort()
		self.xScale = raw.readShort()
		self.yScale = raw.readShort()
		self.redMultiplier = raw.readByte()
		self.greenMultiplier = raw.readByte()
		self.blueMultiplier = raw.readByte()
		self.hue = self.redMultiplier | self.greenMultiplier | self.blueMultiplier
		self.alpha = raw.readUnsignedByte()
		
			
class Point():
	def __init__(self,x=0,y=0):
		self.x = x
		self.y = y

class Layer():
	def __init__(self):
		pass
	def fromRaw(self, raw):
		self.layerId = raw.readInt()
		self.cellsCount = raw.readShort()
		self.cells = list()
		for i in range(self.cellsCount):
			c = Cell()
			c.fromRaw(raw,mapVersion)
			self.cells.append(c)

class Cell():
		def __init__(self):
			pass
		def fromRaw(self, raw, mapVersion):
			self.cellId = raw.readShort()
			self.elementsCount = raw.readShort()
			self.elements = list()
			for i in range(self.elementsCount):
				be = BasicElement.getElementFromType(raw.readByte(),self)
				be.fromRaw(raw,mapVersion)
				self.elements.append(be)

class BasicElement():
	def getElementFromType(type):
		return {ElementTypesEnum.GRAPHICAL:GraphicalElement(), ElementTypesEnum.SOUND:SoundElement()}[type]

class GraphicalElement():
	def fromRaw(self, raw, mapVersion):
		self.elementId = raw.readUnsignedInt()
		self.hue = ColorMultiplicator(raw.readByte(),raw.readByte(),raw.readByte())
		self.shadow =  ColorMultiplicator(raw.readByte(),raw.readByte(),raw.readByte())
		self.offset = Point()
		self.pixelOffset = Point()
		if(mapVersion <= 4):
			self.offset.x = raw.readByte()
			self.offset.y = raw.readByte()
			self.pixelOffset.x = self.offset.x * AtouinConstants.CELL_HALF_WIDTH
			self.pixelOffset.y = self.offset.y * AtouinConstants.CELL_HALF_HEIGHT
		else:
			self.pixelOffset.x = raw.readShort()
			self.pixelOffset.y = raw.readShort()
			self.offset.x = self.pixelOffset.x / AtouinConstants.CELL_HALF_WIDTH
			self.offset.y = self.pixelOffset.y / AtouinConstants.CELL_HALF_HEIGHT
		self.altitude = raw.readByte()
		self.identifier = raw.readUnsignedInt()
		self.calculateFinalTeint()
	def calculateFinalTeint(self):
		r=self.hue.red + self.shadow.red
		g=self.hue.green + self.shadow.green
		b=self.hue.blue + self.shadow.blue
		r = ColorMultiplicator.clamp((r + 128) * 2,0,512)
		g = ColorMultiplicator.clamp((g + 128) * 2,0,512)
		b = ColorMultiplicator.clamp((b + 128) * 2,0,512)
		self.finalTeint = ColorMultiplicator(r,g,b,True)
		
		
class SoundElement():
	def __init__(self):
		pass
	def fromRaw(self, raw, mapVersion):
		self.soundId = raw.readInt()
		self.baseVolume = raw.readShort()
		self.fullVolumeDistance = raw.readInt()
		self.nullVolumeDistance = raw.readInt()
		self.minDelayBetweenLoops = raw.readShort()
		self.maxDelayBetweenLoops = raw.readShort()
		
class ColorMultiplicator():
	def __init__(self, redComponent,greenComponent,blueComponent,forceCalculation=False):
		self.red = redComponent
		self.green = greenComponent
		self.blue = blueComponent
		if((not forceCalculation) and (redComponent + greenComponent + blueComponent == 0)):
			self._isOne = True	
	def clamp(value,mini,maxi):
		if value>maxi:
			return maxi
		if value<mini:
			return mini
		return value

class CellData():
	def __init__(self, map, cellId):
		self.id = cellId
		self._map = map
	def fromRaw(self, raw):
		self._floor = raw.readByte() * 10
		self._losmov = raw.readUnsignedByte()
		self.speed = raw.readByte()
		self.mapChangeData = raw.readUnsignedByte()
		if(self._map.mapVersion > 5):
			self.moveZone = raw.readUnsignedByte()
		if(self._map.mapVersion > 7):
			tmpBits = raw.readByte()
			self._arrow = 15 & tmpBits
			if(self.useTopArrow()):
				self._map.topArrowCell.append(self.id)
			if(self.useBottomArrow()):
			  	self._map.bottomArrowCell.append(self.id)
			if(self.useLeftArrow()):
				self._map.leftArrowCell.append(self.id)
			if(self.useRightArrow()):
				self._map.rightArrowCell.append(self.id)
	def useTopArrow(self):
		return not ((self._arrow & 1) == 0)
	def useBottomArrow(self):
		return not ((self._arrow & 2) == 0)
	def useRightArrow(self):
		return not ((self._arrow & 4) == 0)
	def useLeftArrow(self):
		return not ((self._arrow & 8) == 0)
Je ne suis pas sûr qu'il fonctionne à 100%, mais en tous cas, il ouvre les d2p.
 
Dernière édition par un modérateur:
Inscrit
1 Mai 2014
Messages
20
Reactions
0
#6
Merci beaucoup Labo <3.
 

Sparkdaemon

Staff
Membre du personnel
Inscrit
7 Avril 2009
Messages
556
Reactions
3
#7
La prochaine fois que tu oublie les balises Spoiler, je te botte le fu ! :D
 

Labo

Membre Actif
Inscrit
16 Aout 2013
Messages
799
Reactions
15
#8
Désolé Sparky, il était tard :(
En fait il était minuit, mais j'avais juste oublié l'existence de la balise spoiler et j'étais pas habitué à utiliser la code2 :mrgreen:

@xelit Je n'ai pas inclus le code du module data, c'est volontaire parce qu'il contient mes reader/writer (d'ailleurs un peu moins propres que dans mon tuto sniffer, mais beaucoup plus complet), et que je ne veut pas que cela soit leech par des noobs, mais c'est juste un conteneur pour le type bytes avec des fonctions de lecture que je sais que tu possèdes déjà.
 
Inscrit
1 Mai 2014
Messages
20
Reactions
0
#9
Ok np, le problème vient bien du fait que je décompresse pas les fichiers .dlm avant la lecture. Désolé pour le double post :oops: ! un simple zlib.decompress(data) a résolu le problème !
 

Labo

Membre Actif
Inscrit
16 Aout 2013
Messages
799
Reactions
15
#10
Oui, c'est ce que j'avais dit, on fait tous les mêmes erreurs :)
En tous cas, c'est pas parce que tu as résolu le problème que tu n'as pas besoin de comprendre comment fonctionne le système d'adaptater.
 
Inscrit
1 Mai 2014
Messages
20
Reactions
0
#11
Labo a dit:
En tous cas, c'est pas parce que tu as résolu le problème que tu n'as pas besoin de comprendre comment fonctionne le système d'adaptater.
On est pas tous des fétichistes du savoir ;) mais tu as raison.
 

Labo

Membre Actif
Inscrit
16 Aout 2013
Messages
799
Reactions
15
#12
Non, c'est parce que tu risques d'avoir des surprises sinon, vu que là, tu n'avais pas compris le code ;)
 
Haut Bas