C/C++ [D2P] Decompression ZLIB ?

Inscrit
23 Novembre 2013
Messages
26
Reactions
0
#1
Salut a tous,

Voila, je suis dans la voie de la decompression des cartes D2P, et m'inspirant des sources de EUUBOT j'ai découvert que l'on utilisait un DeflateStream en #.NET. Après m'être renseigné, j'ai découvert qu'en AS3 il semble que Dofus compresse ses cartes avec l'algorithme deflate, de Zlib, mais sans inclure de header. Par ailleurs, EUUBOT supprime les 2 premieres bytes avant d'utiliser son DeflateStream.

J'ai donc utilisé ZLIB et son algorithme inverse inflate, puis j'ai tente ses autres algorithmes, en vain. Peut-être est-ce parceque je ne connais pas la taille de sortie (j'ai essaye de lire les 2 ou 4 premieres bytes en vain) ? J'en défini donc une très grande pour ne pas en manquer d'espace. Lorsque je fais le bilan, je n'ai aucune erreur de la part de Zlib (tous Z_OK), mais à chaque decompression, la sortie semble aléatoire, et n'ai jamais la meme (alors que j'initialise tout mon espace en 0 avant).

Quelqu'un sait-il comment je peux faire en c++, car je désespère....
 

asyade

Membre Actif
Inscrit
26 Avril 2013
Messages
368
Reactions
1
#2
je ni connais pas grand chose en cpp et en zlib mais voila en gros ma fonction de decompression en c#

Code:
byte header = reader.ReadBytes(2);
reader.SetPosition(reader.BaseStream.Length - 16);
int position = reader.ReadUInt();
int mapCount = (int)(reader.ReadUInt());
reader.SetPosition(position);
for (int i = 0; i <= compressedMapsCount; i++)
{
compressedMap = new CompressedMap(reader, d2pFilePath);
if (compressedMap.IsInvalidMap)
continue;
compressedMaps.Add(compressedMap.MapId, compressedMap);
}
le code en du constructeur de CompressedMap

Code:
indexName = reader.ReadUTF();
if (indexName == "link" || indexName == "")
{
isInvalidMap = true;
}
offset = reader.ReadUInt() + 2;
bytesCount = reader.ReadUInt();
a chaque fois le reader c'est un BigEndianReader qui contient touts les bytes du fichier d2p
après il faut décompresser la mape pour sa je te lese regarder dans les fichier de doufs
 
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#3
Tu peux utiliser la balise
Code:
 pour plus de lisibilité ;)
 
Inscrit
23 Novembre 2013
Messages
26
Reactions
0
#4
Asyade, ne t'inquiete pas j'ai deja toute l'architecture et le chargement de compressedMap, mais je n'arrive pas une fois que j'ai ma compressedMap a extraire les données de celle-ci, car le reste est compressé...
 

asyade

Membre Actif
Inscrit
26 Avril 2013
Messages
368
Reactions
1
#5
Ah j'avais pas bien compris ^^' pour decompresser la mape il faut décrypter la mape compresser SANS LE HEADER en md5 via la clef "649ae451ca33ec53bbcbcc33becf15f4" ensuite le hash obtenu tu le convertie en stream puis (en .net) j'utilise la classe DeflateStream pour obtenir un flux sur la mape décompresser x)
 
Inscrit
23 Novembre 2013
Messages
26
Reactions
0
#6
Oui, mais je suis en c++ ;) DeflateStream n'existe pas, et peu de personne savent ce que ca fait en realite derriere...
 
Inscrit
27 Aout 2012
Messages
264
Reactions
0
#7
Ca devrait le faire avec ZLib pourtant.
T'es sur que tu dois utiliser Inflate et pas Deflate ?
 
A

Anonymous

Invité
#8
Deflate est un algorithme de compression de données sans pertes qui couple l'algorithme LZ77 et le codage de Huffman.
Donc à priori il faut bien inflate les données compressées pour les décompresser.

D'après ce que j'ai vu dans les dlm, les deux premiers bytes sont toujours les mêmes. Ça doit être eux qu'il faut sauter avant d'envoyer à inflate.

Pour le "décryptage" de la map, je pense que t'as déjà trouvé ça : https://github.com/Emudofus/Dofus/blob/master2/com/ankamagames/atouin/data/map/Map.as#L170

Si t'as des problèmes avec zlib, ce code marche chez moi (mais je suis sous Qt) :
Code:
QByteArray gUncompress(const QByteArray &data)
{
    if (data.size() <= 4) {
        qWarning("gUncompress: Input data is truncated");
        return QByteArray();
    }

    QByteArray result;

    int ret;
    z_stream strm;
    static const int CHUNK_SIZE = 1024;
    char out[CHUNK_SIZE];

    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = data.size();
    strm.next_in = (Bytef*)(data.data());

    ret = inflateInit2(&strm, 15 +  32); // gzip decoding
    if (ret != Z_OK)
        return QByteArray();

    // run inflate()
    do {
        strm.avail_out = CHUNK_SIZE;
        strm.next_out = (Bytef*)(out);

        ret = inflate(&strm, Z_NO_FLUSH);
        Q_ASSERT(ret != Z_STREAM_ERROR);  // state not clobbered

        switch (ret) {
        case Z_NEED_DICT:
            qWarning("gUncompress: Z_NEED_DICT");
            (void)inflateEnd(&strm);
            return QByteArray();
        case Z_DATA_ERROR:
            qWarning("gUncompress: Z_DATA_ERROR");
            (void)inflateEnd(&strm);
            return QByteArray();
        case Z_MEM_ERROR:
            qWarning("gUncompress: Z_MEM_ERROR");
            (void)inflateEnd(&strm);
            return QByteArray();
        }

        result.append(out, CHUNK_SIZE - strm.avail_out);
    } while (strm.avail_out == 0);

    // clean up and return
    inflateEnd(&strm);
    return result;
}
 
Inscrit
23 Novembre 2013
Messages
26
Reactions
0
#10
Je suis aussi sur Qt j'ai pu tester ton code, le probleme c'est qu'il lit mon data, mais stream.avail_out de change pas, en gros il n'ecrit rien en sortie .... bizarre :(

PS : Merci d'essayer de m'aider les gens :roll:

Caramba a dit:
Deflate est un algorithme de compression de données sans pertes qui couple l'algorithme LZ77 et le codage de Huffman.
Donc à priori il faut bien inflate les données compressées pour les décompresser.

D'après ce que j'ai vu dans les dlm, les deux premiers bytes sont toujours les mêmes. Ça doit être eux qu'il faut sauter avant d'envoyer à inflate.

Pour le "décryptage" de la map, je pense que t'as déjà trouvé ça : https://github.com/Emudofus/Dofus/blob/master2/com/ankamagames/atouin/data/map/Map.as#L170

Si t'as des problèmes avec zlib, ce code marche chez moi (mais je suis sous Qt) :
Code:
QByteArray gUncompress(const QByteArray &data)
{
    if (data.size() <= 4) {
        qWarning("gUncompress: Input data is truncated");
        return QByteArray();
    }

    QByteArray result;

    int ret;
    z_stream strm;
    static const int CHUNK_SIZE = 1024;
    char out[CHUNK_SIZE];

    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = data.size();
    strm.next_in = (Bytef*)(data.data());

    ret = inflateInit2(&strm, 15 +  32); // gzip decoding
    if (ret != Z_OK)
        return QByteArray();

    // run inflate()
    do {
        strm.avail_out = CHUNK_SIZE;
        strm.next_out = (Bytef*)(out);

        ret = inflate(&strm, Z_NO_FLUSH);
        Q_ASSERT(ret != Z_STREAM_ERROR);  // state not clobbered

        switch (ret) {
        case Z_NEED_DICT:
            qWarning("gUncompress: Z_NEED_DICT");
            (void)inflateEnd(&strm);
            return QByteArray();
        case Z_DATA_ERROR:
            qWarning("gUncompress: Z_DATA_ERROR");
            (void)inflateEnd(&strm);
            return QByteArray();
        case Z_MEM_ERROR:
            qWarning("gUncompress: Z_MEM_ERROR");
            (void)inflateEnd(&strm);
            return QByteArray();
        }

        result.append(out, CHUNK_SIZE - strm.avail_out);
    } while (strm.avail_out == 0);

    // clean up and return
    inflateEnd(&strm);
    return result;
}
 
Inscrit
23 Novembre 2013
Messages
26
Reactions
0
#11
Ok, j'ai reussi merci Caramba ta fonction m'a beaucoup aidé ! Juste, comment tu converti ta QString en QByteArray pour la clef de decryptage ? Ma fonction n'a pas l'air de marcher, car apres que j'ai decrypté les données, les valeurs sont aleatoires :/

Code:
QString key;
QList<uchar> _encryptionKey;
 bool ok = true;
        for (int i = 0; i < key.size(); i++)
        {
            if (i % 2 == 0)
                _encryptionKey<<key.mid(i, 2).toUInt(&ok, 16);
        }
 
A

Anonymous

Invité
#13
Salut AnonymHax,

Comme ça fait un peu de temps que j'ai pas touché à ça je te passe directement un morceau de fichier qui marche très bien, comme en témoigne le petit screen à la fin.
Bien sûr si tu veux plus de détails je pourrai t'en donner plus tard ;)

EDIT : pour une raison totalement inconnue ton post s'est affiché dans les nouveaux messages et j'ai donc cru qu'il était tout récent. My bad

Code:
Map::Map(BigEndianReader &reader)
{
    int header = 0;
    int dataLen = 0;
    static const QByteArray decryptionKey("649ae451ca33ec53bbcbcc33becf15f4");

    header = reader.readByte();
    if(header != 77)
        throw MalformedDataException("L'en-tête du fichier dlm est corrompue.");

    _mapVersion = reader.readByte();
    _id = reader.readUnsignedInt();

    if(_mapVersion >= 7)
    {
        _encrypted = reader.readBoolean();
        _encryptionVersion = reader.readByte();
        dataLen = reader.readInt();

        if(_encrypted)
        {
            QByteArray encryptedData = reader.readBytes(dataLen);
            QByteArray decryptedData;

            for(int i = 0; i < encryptedData.size(); i++)
                decryptedData.append(encryptedData[i] ^ decryptionKey[i % decryptionKey.size()]);

            BigEndianReader decryptedReader(decryptedData);
            reader = decryptedReader;
        }
    }

    _relativeId = reader.readUnsignedInt();
    _mapType = reader.readByte();
    _subareaId = reader.readInt();
    _topNeighbourId = reader.readInt();
    _bottomNeighbourId = reader.readInt();
    _leftNeighbourId = reader.readInt();
    _rightNeighbourId = reader.readInt();
    _shadowBonusOnEntities = reader.readInt();
    //qDebug() << "mapVersion =" << _mapVersion;
    qDebug() << "id =" << _id;
    //qDebug() << "relativeId =" << _relativeId;
    //qDebug() << "mapType =" << _mapType;
    //qDebug() << "subareaId =" << _subareaId;
    //qDebug() << "topNeighbourId =" << _topNeighbourId;
    //qDebug() << "bottomNeighbourId =" << _bottomNeighbourId;
    //qDebug() << "leftNeighbourId =" << _leftNeighbourId;
    //qDebug() << "rightNeighbourId =" << _rightNeighbourId;
    //qDebug() << "shadowBonusOnEntities =" << _shadowBonusOnEntities;

    if(_mapVersion >= 3)
    {
        _backgroundRed = reader.readByte();
        _backgroundGreen = reader.readByte();
        _backgroundBlue = reader.readByte();
        _backgroundColor = (_backgroundRed & 255) << 16 | (_backgroundGreen & 255) << 8 | (_backgroundBlue & 255);
        //qDebug("backgroundColor = (%d, %d, %d) = %d", _backgroundRed, _backgroundGreen, _backgroundBlue, _backgroundColor);
    }

    if(_mapVersion >= 4)
    {
        _zoomScale = (float) reader.readUnsignedShort() / 100;
        _zoomOffsetX = reader.readShort();
        _zoomOffsetY = reader.readShort();
            //qDebug() << "zoomScale =" << _zoomScale;
            //qDebug() << "zoomOffsetX =" << _zoomOffsetX;
            //qDebug() << "zoomOffsetY =" << _zoomOffsetY;
    }

    _useLowPassFilter = reader.readBoolean();
    _useReverb = reader.readBoolean();

    if(_useReverb) {
        _presetId = reader.readInt();
    }
    else {
        _presetId = -1;
    }

    //qDebug() << "useLowPassFilter =" << _useLowPassFilter;
    //qDebug() << "useReverb =" << _useReverb;
    //qDebug() << "presetId =" << _presetId;

    int backgroundsCount = reader.readByte();
    //qDebug() << "backgroundsCount =" << backgroundsCount;
    for(int i = 0; i < backgroundsCount; i++) {
        _backgroundFixtures.append(FixtureSP(new Fixture(reader)));
    }

    int foregroundsCount = reader.readByte();
    //qDebug() << "foregroundsCount =" << foregroundsCount;
    for(int i = 0; i < foregroundsCount; i++) {
        _foregroundFixtures.append(FixtureSP(new Fixture(reader)));
    }

    reader.readInt();
    _groundCRC = reader.readInt();
    //qDebug() << "groundCRC =" << _groundCRC;

    int layersCount = reader.readByte();
    //qDebug() << "layersCount =" << layersCount;
    for(int i = 0; i < layersCount; i++) {
        _layers.append(LayerSP(new Layer(reader, _mapVersion)));
    }

    //qDebug() << "cellsCount (const) =" << MAP_CELLS_COUNT;
    for(uint i = 0; i < MAP_CELLS_COUNT; i++) {
        _cells.append(CellDataSP(new CellData(i, reader, _mapVersion)));
    }

    //qDebug("Map parsing done!");
    //qDebug() << "╠═ have errors:" << reader.hasError();
    //qDebug("╠═ bytes remaining: %d", reader.bytesAvailable());
    //qDebug() << "╚═ TOTAL bytes:" << reader.data().size();
}
 
Inscrit
29 Septembre 2011
Messages
393
Reactions
3
#14
Salut, effectivement le sujet remonte un peu, autrement dit si la personne n'à pas trouver de réponse je peus lui faire par de cette source qui à était codé par moi-même ressèment malheureusement il est codé en D mais se rapproche tous de même du c++.
Github : https://github.com/anthony974/MapReader
 
Haut Bas