Ostatnio zmodyfikowano wczoraj o godz. 21:45
tBane Temat założony przez niniejszego użytkownika |
Zapis Binarny wielu obiektów w jednym pliku » 2025-05-03 18:16:32 Cześć. Potrzebuję zapisać binarnie plik Mapy. Plik składa się z kilku części : -Mapa (#MapBegin, #MapEnd) -Budynki (#BuildingBegin, #BuildingEnd) -Postacie (#CharacterBegin, #CharacterEnd) Wymyśliłem, żeby przetwarzać komendy jako stringi i je parsować odpowiednio, ale myślę, że jest optymalniejsze rozwiązanie. czyli taki np. FlatObject "flatObjects\small_sands_rock_2" y=80 x=2528 to: -uint_8t str1_size -char*[str1_size] -uint_16t str2_size -char*[str2_size] -short y -short x Jakbyście Wy się za to zabrali? Poniżej zamieszczam przykładowe dane mapy: #MapBegin Chunk y=0 x=0 1 1 1 1 1 1 1 1 1 1 1 1 39 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 39 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 39 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 39 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 39 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 39 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 39 2 2 2 1 1 1 1 1 1 1 1 1 1 27 28 42 2 2 2 1 1 1 1 1 1 1 1 1 1 39 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 39 2 2 2 2 2 1 1 1 1 1 1 1 1 1 27 42 2 2 2 2 2 1 1 1 1 1 1 1 1 27 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 39 2 2 2 2 2 2 2 1 1 1 1 1 1 1 27 42 2 2 2 2 2 2 2 1 1 1 1 1 1 1 39 2 2 2 2 2 2 2 2 1 1 1 1 1 1 27 42 2 2 2 2 2 2 2 2 Nature "natures\reed_1" y=144 x=2336 FlatObject "flatObjects\small_sands_rock_2" y=80 x=2528 Building "1" y=176 x=2320 Chunk y=0 x=1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 #MapEnd
#BuildingBegin id 1 name "testBuilding" size 16 16 door "doors\wooden_door" top_walls "walls\mud_wall" center_walls "walls\mud_wall" bottom_walls "walls\mud_wall_overgrown" windows "buildings\parts\window_2" 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 0 0 0 2 2 2 2 2 2 0 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 Wall "walls\mud_wall_overgrown" 176 240 Wall "walls\mud_wall_overgrown" 208 240 Wall "walls\mud_wall_overgrown" 240 240 #BuildingEnd
|
|
DejaVu |
» 2025-05-03 18:38:09 Forma zapisu nie ma większego znaczenia. Celem jest działający produkt, a nie wyrafinowana forma pliku. Równie dobrze możesz sobie uprościć sprawę i zapisywać wszystko w JSON-ie. Nawet jeżeli wzrośnie czas odczytu, to możesz do niego wszystko zapisać. Dane binarne w polach JSON-a możesz np. kodować w base64, a pozostałe w formie przyjaznej dla Ciebie (int/string itp). W przypadku JSON-a będziesz miał łatwiej dodawać nowe informacje do pliku, których nie przewidziałeś obecnie. W przypadku pliku binarnego musisz z nieco większą świadomością budować takie pliki, aby np. dało się odczytywać informacje ze starego formatu pliku, gdy rozszerzysz format. W przypadku JSON-a miałbyś to 'out of the box', bo JSON-a zawsze parsujesz poprawnie, natomiast co najwyżej nie obsługujesz nowo wprowadzonych elementów w JSON-ie.
/edit: Natomiast jeżeli barierą jest brak biblioteki do wygodnego czytania i zapisywania JSON-a (typowe wyzwania w C++), to zrób po prostu plik binarny, w którym: - zapisujesz 'ilość plików' - zapisujesz 'nazwę pliku (lub nazwę typu obiektu)' - zapisujesz 'ile danych jest w pliku' - zapisujesz 'zawartość pliku' Wówczas po prostu traktujesz każdą informację jako plik. Mapę, obiekty na scenie itd. Nazwa pliku określa 'co z tym zrobić'. |
|
tBane Temat założony przez niniejszego użytkownika |
» 2025-05-03 18:43:43 Chciałbym ograniczyć rozmiar pliku i przyśpieszyć wczytywanie. Więc pytam czy da się to zoptymalizować i czy mój format (patrz niżej) jest optymalny? FlatObject "flatObjects\small_sands_rock_2" y=80 x=2528 -uint_8t str1_size -char*[str1_size] type -uint_16t str2_size -char*[str2_size] ścieżka -short y -short x //edit ok chyba sobie poradzę .. //edit2 Już wiem jak zrobić. Zamiast zapisywać ścieżki w tej formie lepiej użyć resources_ids czyli listę ścieżek obiektów zapisaną na początku mapy: -uint_8t str1_size -char*[str1_size] ścieżka -uint_8t str1_size -char*[str1_size] typ -uint_16t id_ścieżki -short y -short x |
|
pekfos |
» 2025-05-03 19:01:14 A jaki rozmiar ma ten plik że chcesz optymalizować format? Przejście na plik binarny utrudni ręczne modyfikacje. czyli taki np. FlatObject "flatObjects\small_sands_rock_2" y=80 x=2528 to: -uint_8t str1_size -char*[str1_size] -uint_16t str2_size -char*[str2_size] -short y -short x Chcesz przejść na plik binarny, to opisz dane, a nie jaki format tekstowy masz teraz. Te FlatObject jest pewnie jedną z N możliwości, więc zamiast utrzymywać słowo kluczowe powinieneś mieć bajt mówiący jaki to jest typ obiektu. Nie masz wtedy powtarzającego się tysiąc razy "FlatObject" w pliku. Z pliku tekstowego takie powtórzenia też możesz wyeliminować. Tekst zmiennej długości lepiej przechowywać jako napis z zerem na końcu, a nie kombinować z krótkimi stringami z 8-bitową długością, oraz dłuższymi z 16-bitową. Kiedyś napisałem artykuł na takie tematy: Zapis binarny. |
|
tBane Temat założony przez niniejszego użytkownika |
» 2025-05-03 19:37:06 Napisałem coś takiego: void binary_save( std::wstring pathfile = L"world\\world.wrd" ) { std::ofstream file( pathfile, std::ios::binary ); std::string str; uint8_t str_len; str = "#ResourcesIDs"; str_len = static_cast < uint8_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); uint32_t prefabCount = static_cast < uint32_t >( prefabs.size() ); file.write( reinterpret_cast < const char * >( & prefabCount ), sizeof( uint32_t ) ); for( auto & prefab: prefabs ) { str = prefab->name; str_len = static_cast < uint8_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); } str = "#MapBegin"; str_len = static_cast < uint8_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); uint32_t objects_count; for( auto & chunk: chunks ) { uint8_t chunkX = chunk->coords.x; uint8_t chunkY = chunk->coords.y; file.write( reinterpret_cast < const char * >( & chunkX ), sizeof( uint8_t ) ); file.write( reinterpret_cast < const char * >( & chunkY ), sizeof( uint8_t ) ); Terrain * ter = chunk->terrain; for( short y = 0; y < 16; y++ ) { for( short x = 0; x < 16; x++ ) { file.write( reinterpret_cast < const char * >( & ter->tiles[ y * 16 + x ] ), sizeof( short ) ); } } objects_count = static_cast < uint32_t >( chunk->_natures.size() ); file.write( reinterpret_cast < const char * >( & objects_count ), sizeof( uint32_t ) ); for( auto & nature: chunk->_natures ) { uint8_t value = static_cast < uint8_t >( nature->type ); file.write( reinterpret_cast < const char * >( & value ), sizeof( value ) ); file.write( reinterpret_cast < const char * >( & nature->position.x ), sizeof( float ) ); file.write( reinterpret_cast < const char * >( & nature->position.y ), sizeof( float ) ); } objects_count = static_cast < uint32_t >( chunk->_flatObjects.size() ); file.write( reinterpret_cast < const char * >( & objects_count ), sizeof( uint32_t ) ); for( auto & flat: chunk->_flatObjects ) { uint8_t value = static_cast < uint8_t >( flat->type ); file.write( reinterpret_cast < const char * >( & value ), sizeof( value ) ); file.write( reinterpret_cast < const char * >( & flat->position.x ), sizeof( float ) ); file.write( reinterpret_cast < const char * >( & flat->position.y ), sizeof( float ) ); } } str = "#MapEnd"; str_len = static_cast < uint8_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); file.close(); }
|
|
pekfos |
» 2025-05-03 20:03:28 Powinieneś się przesiąść na GNU nano jako edytor do kodu, tak edukacyjnie. Kopiuj-klej to bodajże Ctrl+U oraz Alt+6, pewnie właśnie by nie kusiło pisać takiej tragedii. Ten kod powinien wyglądać tak: void binary_save( std::wstring pathfile = L"world\\world.wrd" ) { std::ofstream file( pathfile, std::ios::binary ); MojaKlasa mk( file ); mk.write( "#ResourcesIDs" ); mk.write < uint32_t >( prefabs.size() ); for( auto & prefab: prefabs ) { mk.write( prefab->name ); } Implementację klasy zostawiam jako ćwiczenie dla czytelnika. To po prostu opakowanie na to co uporczywie kopiowałeś. Lub pisałeś, nie wiem co gorsze. str_len = static_cast < uint8_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); Przy takim kodzie powinien być generalnie warunek sprawdzający czy długość danych faktycznie nie przekracza 255, w innym razie wygenerowany plik będzie uszkodzony. |
|
tBane Temat założony przez niniejszego użytkownika |
» 2025-05-03 20:11:21 Kurde xD a ja już cały kod napisałem.. poza tym nie wiem jak taką klasę napisać :-/ void binary_save( std::wstring pathfile = L"world\\test_world.wrd" ) { std::ofstream file( pathfile, std::ios::binary ); std::string str; uint16_t str_len; str = "#ResourcesBegin"; str_len = static_cast < uint16_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); uint32_t prefabsCount = static_cast < uint32_t >( prefabs.size() ); file.write( reinterpret_cast < const char * >( & prefabsCount ), sizeof( uint32_t ) ); for( auto & prefab: prefabs ) { str = prefab->name; str_len = static_cast < uint16_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); } str = "#ResourcesEnd"; str_len = static_cast < uint16_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); str = "#MapBegin"; str_len = static_cast < uint16_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); uint32_t objects_count; for( auto & chunk: chunks ) { file.write( reinterpret_cast < const char * >( & chunk->coords.x ), sizeof( int ) ); file.write( reinterpret_cast < const char * >( & chunk->coords.y ), sizeof( int ) ); Terrain * ter = chunk->terrain; for( short y = 0; y < 16; y++ ) { for( short x = 0; x < 16; x++ ) { file.write( reinterpret_cast < const char * >( & ter->tiles[ y * 16 + x ] ), sizeof( short ) ); } } objects_count = static_cast < uint32_t >( chunk->getAllGameObjects().size() ); file.write( reinterpret_cast < const char * >( & objects_count ), sizeof( uint32_t ) ); for( auto & object: chunk->getAllGameObjects() ) { uint8_t type = static_cast < uint8_t >( object->type ); file.write( reinterpret_cast < const char * >( & type ), sizeof( uint8_t ) ); uint32_t object_id = getPrefabID( object->name ); file.write( reinterpret_cast < const char * >( & object_id ), sizeof( uint32_t ) ); if( object->type == GameObjectType::Building ) { short building_id = dynamic_cast < Building * >( object )->id; file.write( reinterpret_cast < const char * >( & building_id ), sizeof( short ) ); file.write( reinterpret_cast < const char * >( & object->position.x ), sizeof( float ) ); file.write( reinterpret_cast < const char * >( & object->position.y ), sizeof( float ) ); } else if( object->type == GameObjectType::Monster ) { sf::Vector2f position = dynamic_cast < Monster * >( object )->base; file.write( reinterpret_cast < const char * >( & position.x ), sizeof( float ) ); file.write( reinterpret_cast < const char * >( & position.y ), sizeof( float ) ); } else if( object->type == GameObjectType::Character ) { file.write( reinterpret_cast < const char * >( & object->position.x ), sizeof( float ) ); file.write( reinterpret_cast < const char * >( & object->position.y ), sizeof( float ) ); } else if( object->type == GameObjectType::InventoryOnMap ) { short inventory_id = dynamic_cast < InventoryOnMap * >( object )->inventory->id; file.write( reinterpret_cast < const char * >( & inventory_id ), sizeof( short ) ); file.write( reinterpret_cast < const char * >( & object->position.x ), sizeof( float ) ); file.write( reinterpret_cast < const char * >( & object->position.y ), sizeof( float ) ); } else { file.write( reinterpret_cast < const char * >( & object->position.x ), sizeof( float ) ); file.write( reinterpret_cast < const char * >( & object->position.y ), sizeof( float ) ); } } } str = "#MapEnd"; str_len = static_cast < uint8_t >( str.size() ); file.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); file.write( str.data(), str_len ); file.close(); }
|
|
pekfos |
» 2025-05-03 20:16:38 poza tym nie wiem jak taką klasę napisać :-/ class MojaKlasa { std::ostream & os; public: MojaKlasa( std::ostream & os ) : os( os ) { } void write( const std::string & str ) { uint16_t str_len = static_cast < uint16_t >( str.size() ); os.write( reinterpret_cast < const char * >( & str_len ), sizeof( str_len ) ); os.write( str.data(), str_len ); } }; |
|
« 1 » 2 |