Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Zapis Binarny wielu obiektów w jednym pliku

Ostatnio zmodyfikowano wczoraj o godz. 21:45
Autor Wiadomość
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
P-182310
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ć'.
P-182311
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
P-182312
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: » Pomoce naukoweZapis binarny artykuł.
P-182313
tBane
Temat założony przez niniejszego użytkownika
» 2025-05-03 19:37:06
Napisałem coś takiego:

C/C++
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 );
   
}
   
   
// save map
   
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 ) {
       
       
// SAVE CHUNK COORDS (sf::Vector2i)
       
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 ) );
       
       
// SAVE TILES
       
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 ) );
           
}
        }
       
       
       
// SAVE NATURES
       
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 ) );
       
}
       
       
// SAVE FLATOBJECTS
       
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();
   
}
P-182314
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:
C/C++
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() ); // szablon, albo osobne metody write8, write16, write32... lepiej nie polegać na automatycznym wyborze żeby było widać co jest jak kodowane patrząc na ten kod.
   
for( auto & prefab: prefabs ) {
       
mk.write( prefab->name );
   
}
   
/// itd...
   
Implementację klasy zostawiam jako ćwiczenie dla czytelnika. To po prostu opakowanie na to co uporczywie kopiowałeś. Lub pisałeś, nie wiem co gorsze.

C/C++
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.
P-182315
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ć :-/

C/C++
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;
   
   
// najpierw zapisz ścieżki prefabrykatów w celu wyeliminowania powtarzalnych scieżek i zastąpieniu ich przez id
   
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 );
   
   
// zapisz mapę - chunki oraz obiekty przypisane do nich
   
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 ) {
       
       
// save chunk coords
       
file.write( reinterpret_cast < const char * >( & chunk->coords.x ), sizeof( int ) );
       
file.write( reinterpret_cast < const char * >( & chunk->coords.y ), sizeof( int ) );
       
       
// save tiles
       
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 ) );
           
}
        }
       
       
       
// save objects
       
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();
   
}
P-182316
pekfos
» 2025-05-03 20:16:38
poza tym nie wiem jak taką klasę napisać :-/
C/C++
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 );
   
}
   
   
//itd inne warianty write()
};
P-182317
« 1 » 2
  Strona 1 z 2 Następna strona