1 Attachment(s)
Dofus Unpacking D2P Archives
Source: [Only registered and activated users can see links. Click Here To Register...]
Language: ActionScript 3 (Not sure) :wacko:
Generally, d2p files looks like splitted archives and each one have some pack of maps in it.
Code:
module Com::AnkamaGames::Jerakine::Resources::Protocols::Impl
class PakProtocol2
include Singleton
attr_accessor :indexes, :properties
def self.method_missing meth, *args
instance.send(meth, *args)
end
def initialize
@indexes = {}
@properties = {}
@adapter = MapsAdapter.new
end
def [](key)
index, subpath = key.to_s.split("/", 2)
read(index, subpath)
end
def read(index, subpath)
i = 0
item = @indexes[index][subpath]
file_stream = item[:stream]
file_stream.pos = item[:offset]
data = file_stream.read(item[:length])
# @adapther is instance of MapsAdapter (see below)
map = @adapter.getResource(data)
end
def parse filepath
raise "[D2P] File doesn't exists #{filepath}" unless File.exists?(filepath)
@stream_name = File.dirname(filepath).gsub("#{RES_ROOT}/", "")
file_stream = nil
unless @indexes[@stream_name]
file_stream = init_stream(filepath)
raise "[D2P] Error while looking for file_stream: #{@stream_name}" unless file_stream
end
end
def init_stream filepath
@indexes[@stream_name] = {}
@properties[@stream_name] = {}
indexes = @indexes[@stream_name]
properties = @properties[@stream_name]
base_pos = 0
base_size = 0
indexes_pos = 0
indexes_size = 0
properties_pos = 0
properties_size = 0
link_filepath = filepath
while File.exists?(link_filepath)
fp = File.open(link_filepath)
raise "[D2P] Incorrect file?!" if fp.read(2) != "\x02\x01"
fp.pos = fp.size - 24
base_pos = fp.readUInt
base_size = fp.readUInt
indexes_pos = fp.readUInt
indexes_size = fp.readUInt
properties_pos = fp.readUInt
properties_size = fp.readUInt
fp.pos = properties_pos
link_filepath = ""
properties_size.times do
prop_type = fp.readUTFString
prop_value = fp.readUTFString
properties[prop_type] = prop_value
if prop_type == "link"
link_filepath = File.join(@stream_name, prop_value)
end
end
fp.pos = indexes_pos
indexes_size.times do
name = fp.readUTFString
offset = fp.readInt
length = fp.readInt
indexes[name] = {
offset: base_pos + offset,
length: length,
stream: fp
}
end
end
return fp
end
end
PakProtocol2.parse "#{RES_ROOT}/maps/maps0.d2p"
end
Chunks of .d2p is .dlm:
As first, they’re packed in GZip, with metadata, and simple inflate isn’t working for it. For more descriptive information visit: ActionScript 3 ByteArray and look at compress method.
Generally, we just need to skip till we get 0x77 byte
Code:
module Com::AnkamaGames::Atouin::Resources::Adapters
class MapsAdapter
def getResource orig_data
data = StringIO.new(orig_data)
header = data.readByte
if header != 77
data.pos = 0
data = StringIO.new(Zlib::Inflate.inflate(data.read))
header = data.readByte
raise "Incorrect header file" if header != 77
end
data.pos = 0
# Map class below :)
map = Map.new
map.parse data
map
end
end
end
Starting reading dlm structure… Map class!
Code:
module Com::AnkamaGames::Atouin::Data::Map
class Map
include Enumerable
attr_accessor :header, :background, :zoomScale, :zoomOffsetX, :zoomOffsetY, :useLowPassFilter, :useReverb, :presetId, :backgroundsCount, :backgroundFixtures, :foregroundsCount, :foregroundFixtures, :cellsCount, :groundCRC, :layersCount, :layers, :cells
def parse data
raise "Incorrect header file" unless data.readByte == 77
@header = {
mapVersion: data.readByte,
id: data.readUInt,
relativeId: data.readUInt,
mapType: data.readByte,
subareaId: data.readInt,
topNeighbourId: data.readInt,
bottomNeighbourId: data.readInt,
leftNeighbourId: data.readInt,
rightNeighbourId: data.readInt,
shadowBonusOnEntities: data.readInt
}
if @header[:mapVersion] >= 3
@background = {
red: data.readByte,
green: data.readByte,
blue: data.readByte
}
@background[:color] = (@background[:red] & 255) << 16 | (@background[:green] & 255) << 8 | @background[:blue] & 255
end
if @header[:mapVersion] >= 4
@zoomScale = data.readUShort / 100
@zoomOffsetX = data.readShort
@zoomOffsetY = data.readShort
end
@useLowPassFilter = data.readByte == 1
@useReverb = data.readByte == 1
@presetId = -1
@presetId = data.readInt if @useReverb
@backgroundsCount = data.readByte
@backgroundFixtures = []
@backgroundsCount.times {
bg = Fixture.new
bg.parse data
@backgroundFixtures << bg
}
@foregroundsCount = data.readByte
@foregroundFixtures = []
@foregroundsCount.times {
fg = Fixture.new
fg.parse data
@foregroundFixtures << fg
}
@cellsCount = 560
data.readInt
@groundCRC = data.readInt
@layersCount = data.readByte
@layers = []
@layersCount.times {
la = Layer.new
la.parse data
@layers << la
}
@cells = []
@cellsCount.times {
cd = CellData.new
cd.parse data
@cells << cd
}
end
end
end
Generally, i don’t care about colors & other info… i just need to get @cells information :)
Fixture, Layer classes
Code:
module Com::AnkamaGames::Atouin::Data::Map
class Fixture
def parse data
@fixtureId = data.readInt
@offset = OpenStruct.new
@offset.x = data.readShort
@offset.y = data.readShort
@rotation = data.readShort
@xScale = data.readShort
@yScale = data.readShort
@redMultiplier = data.readByte
@greenMultiplier = data.readByte
@blueMultiplier = data.readByte
@hue = @redMultiplier | @greenMultiplier | @blueMultiplier
@alpha = data.readUnsignedByte
end
end
end
module Com::AnkamaGames::Atouin::Data::Map
class Layer
def parse data
@layerId = data.readInt
@cellsCount = data.readShort
@cells = []
@cellsCount.times {
c = Cell.new
c.parse data
@cells << c
}
end
end
end
And at last… CellData class!
Code:
module Com::AnkamaGames::Atouin::Data::Map
class CellData
attr_accessor :floor, :losmov, :speed, :mapChangeData
def parse data
@floor = data.readByte * 10;
@losmov = data.readUnsignedByte
@speed = data.readByte
@mapChangeData = data.readUnsignedByte
end
def nonWalkableDuringRP
(@losmov & 128) >> 7 == 1
end
def nonWalkableDuringFight
(@losmov & 4) >> 2 == 1
end
def visible
(@losmov & 64) >> 6 == 1
end
def farmCell
(@losmov & 32) >> 5 == 1
end
def blue
(@losmov & 16) >> 4 == 1
end
def red
(@losmov & 8) >> 3 == 1
end
def los
(@losmov & 2) >> 1 == 1
end
def mov
(@losmov & 1) == 1
end
end
end
You can detect “move-ability” of cell using methods: mov, nonWalkableDuringRP and nonWalkableDuringFight.
Usage
Code:
map_id = 21760516
map_path = "maps/#{map_id % 10}/#{map_id}.dlm"
map = PakProtocol2[map_path]
puts map.cells[0].mov # true or false, means movable?
puts map.cells.select { |c| c.mov } # select all movable cells
Output
Code:
true
[
#<Com::AnkamaGames::Atouin::Data::Map::CellData:0x0001 ...>,
#<Com::AnkamaGames::Atouin::Data::Map::CellData:0x0002 ...>,
#<Com::AnkamaGames::Atouin::Data::Map::CellData:0x0003 ...>,
#<Com::AnkamaGames::Atouin::Data::Map::CellData:0x0004 ...>,
#<Com::AnkamaGames::Atouin::Data::Map::CellData:0x0005 ...>,
#<Com::AnkamaGames::Atouin::Data::Map::CellData:0x0006 ...>
]
DofusInvoker (Game Flash Loader) Decompile with Sothink SWF Decompiler or something :)