Przepisanie funkcji z C# do C++ (QT5)

0

Witam, chciałbym odczytać sprite o danym ID z pliku Tibia.spr (Tak chodzi o Tibie)
Znalazłem funkcję napisaną w C# lecz chciałbym przepisać ja do C++/QT5

// Thanks to OpiF at http://otfans.net/showthread.php?t=102065
        // and to Thomac at http://otfans.net/showthread.php?t=141982
        public static Image GetSpriteImage(string file, int spriteId)
        {
            if (spriteId < 2)// || spriteId > 28722)
                throw new ArgumentOutOfRangeException("spriteId");

            int size = 32;
            Bitmap bitmap = new Bitmap(size, size);

            using (BinaryReader reader = new BinaryReader(File.OpenRead(file)))
            {
                ushort currentPixel = 0;
                long targetOffset;

                reader.BaseStream.Seek(6 + (spriteId - 1) * 4, SeekOrigin.Begin);
                reader.BaseStream.Seek(reader.ReadUInt32() + 3, SeekOrigin.Begin);

                targetOffset = reader.BaseStream.Position + reader.ReadUInt16();

                while (reader.BaseStream.Position < targetOffset)
                {
                    ushort transparentPixels = reader.ReadUInt16();
                    ushort coloredPixels = reader.ReadUInt16();
                    currentPixel += transparentPixels;
                    for (int i = 0; i < coloredPixels; i++)
                    {
                        bitmap.SetPixel(
                            currentPixel % size,
                            currentPixel / size,
                            Color.FromArgb(reader.ReadByte(), reader.ReadByte(), reader.ReadByte())
                        );
                        currentPixel++;
                    }
                }
            }

            return bitmap;
        }

Jak to przepisać, wiele razy próbowałem lecz nie moge znaleźć podobnych funkcji w QT :/

0

lecz nie moge znaleźć podobnych funkcji w QT
Której konkretnie?
Kod w C# jest raczej zrozumiały i samokomentujący.
Masz tu po pierwsze operacje na pliku (binarnym) – jak to zrobić w Qt to łatwo znajdziesz, zresztą masz też do dyspozycji STL i bibliotekę standardową C.
A po drugie bitmapę, składaną piksel po pikselu.

Szukaj informacji na te dwa tematy, a nie dokładnych odpowiedników każdej funkcji...

0

Problem dotyczy przesunięć (seek). Jakaś pomoc dotycząca tej funckji w QFile ? Czy funkcja seek w QFile zawsze zaczyna od początku i trzeba podawać pozycje przy każdym przesunieciu czy jak?

0

Jeśli się nie walnąłem to powinno być tak

QImage readSpriteFromDataStream(QDataStream &data) {
    const int size = 32;
    QImage bitmap(size, size, QImage::Format_ARGB32);
    bitmap.feel(0x0); // black fully transparent

    quint16 dataSize;
    reader >> dataSize;
    int readCount=sizeof(dataSize), currentPixel=0;

    while (readCount<dataSize) {
         quint16  transparentPixels, coloredPixels;
         data >> transparentPixels >> coloredPixels;
         readCount += sizeof(transparentPixels) + sizeof(coloredPixels) + coloredPixels * 3;
         currentPixel += transparentPixels;

         for (int i = 0; i < coloredPixels; i++) {
               quint8 r,g,b;
               data >> r >> g >> b;
               bitmap.setPixel(currentPixel % size, currentPixel / size, qRgba(r,g,b, 0xff));
               currentPixel++;
         }
    }
   Q_ASSERT(readCount==dataSize); // data coherency 
    return bitmap;
}

QImage getSpriteImageFormFileNamed(const QString &file, int spriteId)  {
    if (spriteId < 2) {
         return QImage();
    }

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly))
         return;

    QDataStream reader(&file);
    reader.setByteOrder(QDataStream::LittleEndian); // zakładam że format pliku jest związany x86, więc ma być mała endiana
    int locationOfSpriteOffset = 6 + (spriteId - 1) * 4;
    reader.skipRawData(locationOfSpriteOffset);
    quint32 spriteOffset;
    reader >> spriteOffset;
    file.seek(spriteOffset+3); // czy te 3 bajty nie zawierają jakiegoś znacznika lub innej użytecznej informacji?
    return readSpriteFromDataStream(reader);
}
0

Wykorzystałem Twój kod.
Otóż poprawiłem pare rzeczy.
Wygląda to tak:

QImage MainWindow::readSpriteFromDataStream(QDataStream &data) {
    const int size = 32;
    QImage bitmap(size, size, QImage::Format_ARGB32);
    bitmap.fill(0x0); // black fully transparent

    quint16 dataSize;
    data >> dataSize;
    int readCount=sizeof(dataSize), currentPixel=0;

    while (readCount<dataSize) {
         quint16  transparentPixels, coloredPixels;
         data >> transparentPixels >> coloredPixels;
         readCount += sizeof(transparentPixels) + sizeof(coloredPixels) + coloredPixels * 3;
         currentPixel += transparentPixels;

         for (int i = 0; i < coloredPixels; i++) {
               quint8 r,g,b;
               data >> r >> g >> b;
               bitmap.setPixel(currentPixel % size, currentPixel / size, qRgba(r,g,b, 0xff));
               currentPixel++;
         }
    }
   Q_ASSERT(readCount==dataSize); // data coherency
    return bitmap;
}

QImage MainWindow::getSpriteImageFormFileNamed(const QString &fileName, int spriteId)  {
    if (spriteId < 2) {
         return QImage();
    }

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly))
         return QImage();

    QDataStream reader(&file);
    reader.setByteOrder(QDataStream::LittleEndian); // zakładam że format pliku jest związany x86, więc ma być mała endiana
    int locationOfSpriteOffset = 6 + (spriteId - 1) * 4;
    reader.skipRawData(locationOfSpriteOffset);
    quint32 spriteOffset;
    reader >> spriteOffset;
    file.seek(spriteOffset+3); // czy te 3 bajty nie zawierają jakiegoś znacznika lub innej użytecznej informacji?
    return readSpriteFromDataStream(reader);
}

Linia 36 to Q_ASSERT(readCount==dataSize); // data coherency i tu wywala

0

Asercja wywala, bo albo nadal jest coś źle, albo kod wyjściowy ma jakieś błędy.
Ta asercja sprawdza czy nie dokonano czytania poza planowaną wielkość bloku (można ją wywalić, ale lepiej ustalić czemu jest źle).
Np poprawiając asercję w ten sposób:

Q_ASSERT(readCount==dataSize, 
       "czytanie spritea",
       QString("Dane są niekonsystentne przeczytano za dużo o %1 bajtów ").arg(readCount-dataSize).toAscii().data());
0

"ASSERT failure in czytanie spritea: "Dane s? niekonsystentne przeczytano za du?o o 2 bajt?w ", file mainwindow.cpp, line 36"

Użyłem:
Q_ASSERT_X(readCount==dataSize, "czytanie spritea", QString("Dane są niekonsystentne przeczytano za dużo o %1 bajtów ").arg(readCount-dataSize).toLocal8Bit().data());

0

sprawdź czy kod wyjściowy z C# daje tą samą różnice (jeśli tak to tam też jest błąd, który nie przynosi poważnych konsekwencji).
Chodzi o różnicę: reader.BaseStream.Position - targetOffset po pętli while.
Dalej już sam ustalaj czy to błąd z kodu C# czy błąd nastąpił przy konwersji kodu. I tak już dostałeś więcej niż chciałeś. Możesz też pójść na lenia i wywalić asercję.

0
int locationOfSpriteOffset = 6 + (spriteId - 1) * 4;

Jeżeli używasz Tibia.spr w wersji powyżej 9.7 (lub coś koło tego) to ilość sprite jest zapisana na 4 bajtach, a nie na dwóch. Wynika to z faktu, iż CipSoft przekroczył limit 2 bajtów (65535) i nie dałoby rady wrzucić więcej obrazków. Więc będzie 8 zamiast 6.

Jeszcze jedno, 3 bajty o które pytałeś to kolor, który ma służyć jako transparent. We wszystkich jest to magenta (255, 0, 255).

0

Ustawiłem 8 zamiast 6... Pusty obraz.. Zostawilem 6... Wszystko dziala, tylko caly czas mysle o tych dwóch bajtach.. zaraz sprawdze w C# jak to jest.

Sprawdzilem i ... 7858612 =/= 7858610
reader.BaseStream.Position wynosi 7858612
a targetOffset wynosi 7858610

0

Ja zakładam, że to jest problem z kodem C#
Radzę dorwać jakąś dokumentację o formacie danych tego pliku, może @Dantez ma linka do dokumentacji i się nią podzieli.

0

Różnica dwóch bajtów wynika z:

int readCount = sizeof(dataSize);

Te 2 bajty, które tam są, nie wliczają się w wielkość samego sprite.

0

Zrobiłem:
int readCount=sizeof(dataSize)-2
i z ASSERT juz problemu nie ma..
a czy jeśli zrobie tak:
dataSize += 2;
a w readCount zostawie jak bylo, nie bedzie problemu?
Sprite sie pokazuje ale chodzi mi o bajty itd itp..

0
Dantez napisał(a):

Różnica dwóch bajtów wynika z:

int readCount = sizeof(dataSize);

Te 2 bajty, które tam są, nie wliczają się w wielkość samego sprite.

Skoro tak to błąd jest w kodzie C#, bo te +2 (sizeof(dataSize)) wzięło się z tej linijki:

targetOffset = reader.BaseStream.Position + reader.ReadUInt16();

a jako, że reader.BaseStream.Position jest pierwsze w stosunku do reader.ReadUInt16() to znaczy, że autor kodu C# uwzględnia rozmiar pola rozmiaru w targetOffset.

0

czyli te +2 dodać do dataSize czy odjąć z readCount ???

0
MarekR22 napisał(a):

a jako, że reader.BaseStream.Position jest pierwsze w stosunku do reader.ReadUInt16() to znaczy, że autor kodu C# uwzględnia rozmiar pola rozmiaru w targetOffset.

Albo, że od wartości na tych dwóch bajtach odjęto jego wielkość.

@aifam96

int readCount = sizeof(dataSize);

Zamień na:

int readCount = 0;
0

@Dantez "Dane s? niekonsystentne przeczytano za du?o o 4224954 bajt?w." xd

0

Sprawdzaj, czy spriteOffset jest równy zero, są to puste sprity.

@aifam96
U mnie skopiowany stąd i poprawiony kod działa.

0

@Dantez nie działa. zostaawie jak było i chyba dodam czy usune 2 bajty bo bylo dobrze :)

0

Wystaw mi ten plik, to to sprawdzę.
IMO coś musiałeś przekręcić, bo z tego co napisałeś wcześniej, wynika że poprawienie tej jednej linijki powinno załatwić sprawę asercji (swoją drogą jest to doskonały przykład jak asercjami można wykrywać błędy logiczne w kodzie).


Dobra znalazłem taki opis tego pliku: http://tpforums.org/forum/threads/5031-Tibia-Sprite-File-Structure Z tego wynika, że kod w C# jest nieprawidłowy! NIE MA INFORMACJI O CAŁKOWITYM ROZMIARZE spritea, bo jest to zbędna informacja (sprite ma zawsze 1024 pikseli, więc wiadomo kiedy skończyć).
1
MarekR22 napisał(a):

sprite ma zawsze 1024 pikseli, więc wiadomo kiedy skończyć

Ostatnie przeźroczyste piksele są pomijane, także zmienna currentPixel może mieć wartość np. 823, a sprite będzie już załadowany.

@aifam96

QImage readSpriteFromDataStream(QDataStream &data)
{
    QImage bitmap(32, 32, QImage::Format_ARGB32);
    bitmap.fill(0x0); // Transparent

    quint16 dataSize;
    data >> dataSize;

    quint32 readCount=0, currentPixel=0;

    while(readCount < dataSize)
    {
         quint16 transparentPixels, coloredPixels;
         data >> transparentPixels >> coloredPixels;
         readCount += sizeof(transparentPixels) + sizeof(coloredPixels) + coloredPixels * 3;
         currentPixel += transparentPixels;

         for(int i = 0; i < coloredPixels; i++)
         {
               quint8 r,g,b;
               data >> r >> g >> b;
               bitmap.setPixel(currentPixel % 32, currentPixel / 32, qRgba(r,g,b, 0xff));
               currentPixel++;
         }
    }

    return bitmap;
}

QImage getSpriteImageFormFileNamed(const QString &fileName, int spriteId)
{
    if(spriteId < 2)
        return QImage();

    QFile file(fileName);
    if(!file.open(QIODevice::ReadOnly))
         return QImage();

    QDataStream reader(&file);
    reader.setByteOrder(QDataStream::LittleEndian);
    reader.skipRawData(6 + (spriteId - 1) * 4);

    quint32 spriteOffset;
    reader >> spriteOffset;

    if(spriteOffset == 0)
        return QImage(); // Blank sprite

    file.seek(spriteOffset+3);
    return readSpriteFromDataStream(reader);
}

Działa, sprawdzone.

2

W tej tabeli z linka jest błąd (w tekście można jednak to doczytać, że rozmiar jest dostępny).
Naskrobałam przeglądarkę i działa bez zarzutu (sprawdzam dokładnie poprawność formatu i wszystko się zgadza).

0

@MarekR22 Po załadowaniu pliku Tibia.spr, wywala mi pełno : "Unexpected color (00,00,ff) for transparency" w logach... jednakże wszystko się ładuje...

Dobra to jest nie ważne, teraz co lepsze? Ładować wszystkie sprity do pamięci czy na bieżąco ładować(podaje ID Sprita i ładuje do QImage)?

0

Ok, zrobiłem odczytywanie jednego sprita z pliku... Plik tibia.spr z wersji 8.60

QImage Utils::getSpriteImage(const QString &fileName, int spriteId)
{
    QFile file(fileName);

    if (!file.open(QIODevice::ReadOnly)) {
        qWarning("Failed to open sprite file: %s", fileName.toLatin1().data());
        return QImage();
    }

    QDataStream data(&file);
    data.setByteOrder(QDataStream::LittleEndian);

    quint32 version;
    data >> version;

    if (version!=1303190066) {
        qDebug("This file version: %d might be not supprted.", version);
    }

    quint16 spritesCount;
    data >> spritesCount;

    if(spritesCount < spriteId)
        return QImage();

    data.skipRawData((spriteId - 1) * 4);

    quint32 spriteOffset;
    data >> spriteOffset;

    file.seek(spriteOffset);

    if(spriteOffset == 0)
        return QImage();

    quint8 r, g, b;
    data>>r>>g>>b; // transparency color - not needed when alpha is used

    if (r!=0xff || g!=0 || b!=0xff) {
        qWarning("Unexpected color (%02x,%02x,%02x) for transparency", r, g, b);
    }

    QImage image(SpriteSize, SpriteSize, QImage::Format_ARGB32);
    image.fill(0x0);

    quint16 spriteSize;
    data >> spriteSize;
    int readCount = 0;
    int pixelNumber = 0;
    while (readCount<spriteSize) {
        quint16 transparentCount, coloredCount;
        data >> transparentCount >> coloredCount;
        readCount += sizeof(transparentCount) + sizeof(coloredCount) + coloredCount*3;
        pixelNumber += transparentCount;
        for (int i=0; i<coloredCount; ++i) {
            data>>r>>g>>b;
            image.setPixel(pixelNumber%SpriteSize,
                pixelNumber/SpriteSize,
                qRgba(r,g,b,0xff));
            pixelNumber++;
        }
    }

    Q_ASSERT(readCount==spriteSize);

    return image;
}

ale w konsoli mam to:

This file version: 1277298068 might be not supprted.
Unexpected color (f8,f8,f0) for transparency

Co jest jeszcze źle? Jak mam sprawdzić czy wyświetla prawidłowego sprita z Tibii..?

0

Dantez pisał(a), że różne wersje mogą mieć różne formaty (np inny rozmiar pól). Jeśli asercja zawiedzie będziesz to poprzedzi ją informacja o nieznanym identyfikatorze wersji. Ostrzeżenie o kolorze to już moja nadgorliwość, można je wywalić.
Moja rada, wykaż się troszkę inwencją, bo to nie może się skończyć tak, że luzie z forów będą ci dawać kod, a potem ty będziesz twierdził, że jesteś autorem.
Dla mnie temat zamknięty, bo dostałeś dużo więcej niż prosiłeś, a teraz dopominasz się o jakieś drobiazgi, które wymagają choć odrobiny własnej inwencji i odrobiny umiejętności.

0

Dziękuję Wam bardzo, za pomoc, wiem że dostałem więcej niż chciałem ale to tylko dzięki Wam @MarekR22 i @Dantez. Więc prosze @MarekR22 nie wypominaj mi. "bo to nie może się skończyć tak, że luzie z forów będą ci dawać kod" .. Sam wyjechałeś z nim gdy zapytałem o to jak działa funckja seek w QFile, nikt nie prosił "Ejj, przepiszcie mi to" albo coś.. Nie będę twierdzić, że jestem autorem kodu, gdyż szanuje prace innych oraz pomoc.

1 użytkowników online, w tym zalogowanych: 0, gości: 1