Blog (35)
Komentarze (398)
Recenzje (0)

Notatki programisty: integracja PhysicsFS z SFML

Strona główna@biomenNotatki programisty: integracja PhysicsFS z SFML
03.01.2017 11:50

W poniższym wpisie chciałbym przedstawić prosty sposób na wykorzystanie plików z rozszerzeniem .zip do przechowywania zasobów, w aplikacji używającej SFML. Motywacją do wpisu był fakt że przeszukując internet, nie znalazłem gotowego działającego kawałka kodu który zapewniłby mi taką funkcjonalność. Przechodząc do konkretów, zapraszam do lektury.

PhysicsFS

Do odczytu plików z archiwum .zip będziemy używać biblioteki PhysicsFS. Jest to otwarto-źródłowa bibliotek wydana na licencji zlib dzięki czemu możemy być pewni że nikt nie będzie wołać o otwarcie naszego kodu. Mimo iż ostatnia aktualizacja miała miejsce w 2012 roku, format .zip jest na tyle powszechny i uniwersalny że nie jest to jakaś duża niedogodność. Co prawda można ubolewać nad brakiem rozwoju projektu gdyż, prawdopodobnie, nie doczekamy się w nim funkcjonalności zabezpieczenia archiwum, hasłem, ale na potrzeby prostej aplikacji sprawdzi się w sam raz.

bDUNcQDH

Biblioteka jest rozpowszechniana w postaci kodu źródłowego, co oznacza że sami musimy ją zbudować. W poniżej przedstawionym przykładzie, do zbudowania kodu używam QtCreatora z MinGW ze skonfigurowanym Cmakem w IDE. Uważam to za bardzo wygodne połączenie gdyż całość odbywa się w jednym oknie. Instrukcję jak skonfigurować Cmake w QtCreatorze znajdziemy tutaj.

Mając przystosowane środowisko, otwieramy plik CmakeLists.txt, projekt zostaje otworzony. Wybieramy typ budowy na release gdyż interesuje nas wersja bez znaczników debugowania. Przechodząc do do zakładki Projekty, mamy możliwość spersonalizowania budowanej biblioteki. Możemy wybrać jakie formaty mają być wspierane itp. PhysicsFS poza .zip’em, oferuje wsparcie dla kilku innych rozszerzeń, więcej informacji znajduje się na stronie projektu. Wracając do tematu głównego: zatwierdzamy konfigurację i budujemy projekt.

235065

Jako że wybrałem w konfiguracji opcje OFF w polu PHYSFSBUILDSTATIC, interesującym mnie wynikiem kompilacji będą pliki: libphysfs.dll oraz libphysfs.dll.a. Następnie dobrze jest uporządkować katalog bibliotek. Wyżej wspomniane pliki przerzucamy do katalogu o nazwie lib, nagłówki ze źródeł PhysicFS przenosimy do nowego katalogu o nazwie: include, całość zamykamy w folderze o nazwie physicfs.

Integracja z SFML.

Pozwolę sobie nie opisywać procesu budowania SFML’a pod QtCreatorem. Jest to dobrze wytłumaczone w podanym przeze mnie linku dotyczącym konfiguracji Cmake’a lub też jak kto woli: analogiczny do budowania PhysicsFS. Na potrzeby przykładowej aplikacji utworzymy archiwum o następującej strukturze.

bDUNcQDN
data | | --- > textures (katalog zawierający pliki cpp.png oraz uszatek.png) | --- > audio (katalog zawierajacy neverwinter_theme.ogg )

W archiwum będziemy przechowywać najpopularniejsze zasoby z których korzystają proste gry indie czyli tekstury i audio. Tworzymy projekt w QtCreatorze, dołączamy zależności i możemy przystąpić do tego co tygryski lubią najbardziej. Czyli kodu. ;) Poniżej przedstawione zostało jak będą wyglądać nasze funkcje ładujące pliki z archiwum.

/ Funkcja ladowania tekstur z objasnieniem. /sf::Texture getTextureFromZip(std::string name){ sf::Texture texturePtr = nullptr;

/ Test czy plik istnieje w obszarze poszukiwan./ if(PHYSFSexists(name.cstr())) { std::cout << name + " - exists. \n";

bDUNcQDO

/ Otwieramy plik i przypisujemy do niego wskaznik. Nastepnie odczytujemy dane do buffora oraz ilosc danych (jak duzo ich jest) / PHYSFSFile fileFromZip = PHYSFSopenRead(name.cstr());

const int fileLenght = PHYSFSfileLength(fileFromZip); char buffer = new charfileLenght; int readLength = PHYSFSread (fileFromZip, buffer, 1, fileLenght);

/ Tworzymy obiekt SFML w pamieci. / texturePtr = new sf::Texture(); texturePtr->loadFromMemory(buffer, readLength);

/ Nie trzeba chyba tlumaczyc, prawda? ;) /  PHYSFSclose(fileFromZip);

bDUNcQDP

} else { std::cout << name + " - doesn't exists. \n";  }

return texturePtr; }

/ Analogiczna funkcja dla plikow audio. / sf::SoundBuffer getSoundBufferFromZip(std::string name) { sf::SoundBuffer soundBufferPtr = nullptr;

if(PHYSFSexists(name.cstr()))  { std::cout << name + " - exists. \n";

bDUNcQDQ

PHYSFSFile fileFromZip = PHYSFSopenRead(name.cstr());

const int fileLenght = PHYSFSfileLength(fileFromZip); char buffer = new charfileLenght; int readLength = PHYSFSread (fileFromZip, buffer, 1, fileLenght);

soundBufferPtr = new sf::SoundBuffer(); soundBufferPtr->loadFromMemory(buffer, readLength);

PHYSFSclose(fileFromZip);

} else { std::cout << name + " - doesn't exists. \n";  }

return soundBufferPtr; } /code

Myślę że komentarz zawarty w kodzie jest wystarczająco wyczerpujący. Poniżej kod naszej funkcji głównej:

int main(int argc, char argv){ / Zainicjuj PHYSFS i dodaj plik .zip do "obszaru poszukiwań". / PHYSFSinit(nullptr); PHYSFSaddToSearchPath("data.zip", 1);

/ Funkcje zwracaja wskazniki wiec warto sie zabezpieczyc przed ewentualnym zapomnieniem o dealokacji pamieci. Stad std::uniqueptr. Zakladamy ze funkcje nie zwroci nam nullptr stad brak testu. Co moze pojsc zle? ;) / std::uniqueptr textureCpp(  getTextureFromZip("texture/cpp.png"));

std::uniqueptr textureUszatek( getTextureFromZip("texture/uszatek.png"));

std::uniqueptr soundBufferNeverwinter( getSoundBufferFromZip("audio/neverwintertheme.ogg"));

/ Po zakonczeniu pracy z PHYSFS nalezy go zdeinicjalizowac. /  PHYSFSdeinit();

/ Standardowy kod apki SFML. / sf::RenderWindow window(sf::VideoMode(600, 400, 32), "AppZip", sf::Style::Default);

sf::RectangleShape shapeCpp; shapeCpp.setSize(sf::Vector2f(150,150)); shapeCpp.setPosition(sf::Vector2f(50, 50));  shapeCpp.setTexture(textureCpp.get());

sf::RectangleShape shapeUszatek; shapeUszatek.setSize(sf::Vector2f(150,150)); shapeUszatek.setPosition(sf::Vector2f(300, 50));  shapeUszatek.setTexture(textureUszatek.get());

sf::Sound soundNeverwinterTheme; soundNeverwinterTheme.setBuffer(soundBufferNeverwinter.get());  soundNeverwinterTheme.play();

while(window.isOpen())  { sf::Event myEvent;  while(window.pollEvent(myEvent)){

if(myEvent.type == sf::Event::Closed){ window.close(); }  }

window.clear();  window.draw(shapeCpp); window.draw(shapeUszatek); window.display();  }

return 0; } /code

Tu tak samo jak w przypadku kodu funkcji, myślę że komentarze wyczerpują temat. Po zbudowaniu aplikacji wszystko powinno wyglądać a nawet śpiewać. ;) Niestety na blogu nie zaprezentuje działającego dźwięku przez co zachęcam do własnoręcznego sprawdzenia kodu.

235102

Podsumowanie.

W ten oto sposób dobrnęliśmy do końca. Na początku wpisu wspomniałem że biblioteka nie jest już rozwijana i prawdopodobnie nie zostanie w niej zaimplementowany mechanizm zabezpieczania archiwum hasłem. Na pocieszenie, wspomnę o tym że jeden z najlepszych hack’n’slashy ostatnich lat, Torchlight, przechowywał swoje zasoby w niezabezpieczonym pliku .zip o ile dobrze pamiętam. Jak widać, można w komercyjnym produkcie zawierać tak "trywialne" rozwiązania.

Co więcej mógłbym zaproponować to jeżeli ktoś chciałby korzystać z PhysicsFS w większej aplikacji to warto jest go „ucywilizować” i opakować klasą ze spójnym interfejsem. Korzystanie z biblioteki w stylu strukturalnym na dłuższą metę wydaje się niewygodne i mało praktyczne.

Dzięki za uwagę. ;)

Udostępnij:
bDUNcQED