[C++, SDL2, Rapid XML] bad_alloc
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!

[C++, SDL2, Rapid XML] bad_alloc

AutorWiadomość
Temat założony przez niniejszego użytkownika
[C++, SDL2, Rapid XML] bad_alloc
» 2019-04-26 12:01:31
Witam, mam problem z kodem. Czasami (zdawać by się mogło losowo) - mój program po prostu nie wczytuje tile_type (czasami nie wczyta ostatniego (~15% na to), a czasami nie wczyta jakichś 2 (jak na razie tylko raz mi się to zdarzyło) ). Teraz dodatkowo dostaję bad_alloc gdy próbuję wczytać mapę. Nie wiem, co jeszcze powiedzieć.. Wrzucę kod, który nie trawi reszty kodu i loga z mojego programu.

Czytałem o tym, co to bad_alloc, widziałem kilka tematów dotyczących tego, dowiedziałem się że rapid_xml dziedziczy z memory_pool, ale nadal nie wiem co zepsułem w swoim programie.

Wydaje mi się że to może jakiś memory leak, ale wszystko co stworzyłem z new i przechowuję w klasie "Bank" nie powinno mieć tego problemu. Może używam rapidxml a nie prawidłowo / nie usuwam czegoś, z czego nie zdaję sobie sprawy że się w ogóle alokuje...

W zasadzie chyba największe zagęszczenie jest w funkcji load_world_xml, bo woła ona
        load_Tile_type_xml(path,world.bank_tile_type,images) &&
        load_Resource_xml(path,world.bank_resource,images) &&
        load_Settlement_type_xml(path,world.bank_settlement_type,images) &&
        load_Building_xml(path,world.bank_building,images)
- czy po zakończeniu każdej z tych "podfunkcji" pamięć użyta tej konkretnej zwalnia się od razu? Chyba nie powinno być problemu związanego z tym.

Czytałem też o tym, że rapidxml nie "posiada" std::stringów, które się używa ale nie rozumiem na czym polegał problem w tamtym temacie. Ale wiem, że używam bardzo dużo stringów - każdy bank ma dla każdego elementu string z nazwą.

Próbowałem też odpalić program po wyłączeniu przeglądarki (1.5GB ramu więcej) ale efekt był dokładnie ten sam, więc to nie kwestia ilości ramu. Mam też dosyć stary laptop (chyba z 9 lat już będzie miał niedługo) ale naprawdę wątpię, żeby to było coś innego niż kwestia programowa.


C/C++
#include "XML_Parser.h"

#include "rapidxml-1.13/rapidxml.hpp"
#include "rapidxml-1.13/rapidxml_utils.hpp"
#include <fstream>

#include "World.h"
#include <stdint.h>


//{ some helpers
bool open( rapidxml::xml_document <>& doc, std::string path )
{
    try
    {
        rapidxml::file <> file(( path ).c_str() );
        doc.parse < 0 >( file.data() );
    }
    catch( const std::runtime_error & e )
    {
        ERR( "(" << path << ")" << " Runtime error: " << e.what() EL );
        return false;
    }
    catch( const rapidxml::parse_error & e )
    {
        ERR( "(" << path << ")" << " Parse error: " << e.what() EL );
        return false;
    }
    catch( const std::exception & e )
    {
        ERR( "(" << path << ")" << " Error: " << e.what() EL );
        return false;
    }
    catch(...)
    {
        ERR( "An unknown error occurred." EL );
        return false;
    }
    return true;
}
bool value_string_node( rapidxml::xml_node <> * parent, std::string name, std::string & target, bool silent = false )
{
   
    rapidxml::xml_node <> * temp_node = parent->first_node( name.c_str() );
   
    if( temp_node == nullptr )
    {
        if( !silent )
             ERR( "Couldn't find " << name << "!" EL );
       
        return false;
    }
    else
    {
        //DEB(name<<" = "<<target EL);
        target = temp_node->value();
        return true;
    }
}
bool value_long_node( rapidxml::xml_node <> * parent, std::string name, long long & target, bool silent = false )
{
    std::string v;
    if( value_string_node( parent, name, v, silent ) ); // fine
    else return false; //fail
   
    //DEB(name<<" = "<<target EL);
    target = std::stoll( v );
    return true;
}
int range( long long number, long long vmin, long long vmax, std::string name = "", bool silent = false )
{
    if( number < vmin )
    {
        if( name != "" )
        {
            if( !silent )
                 ERR( name << "(" << number << ") must be greater than : " << vmin EL );
           
        }
        return - 1;
    }
    if( number > vmax )
    {
        if( name != "" )
        {
            if( !silent )
                 ERR( name << "(" << number << ") must be smaller than : " << vmax EL );
           
        }
        return 1;
    }
    return 0;
}
bool value_int16_node( rapidxml::xml_node <> * parent, std::string name, int16_t & target, bool silent = false )
{
    long long v;
    if( value_long_node( parent, name, v, silent ) ); // fine
    else return false; //fail
   
    if( range( v, INT16_MIN, INT16_MAX, name, silent ) )
         return false;
    else
    {
        target = v;
        //DEB(name<<" = "<<target EL);
        return true; //success
    }
}
bool value_uint16_node( rapidxml::xml_node <> * parent, std::string name, uint16_t & target, bool silent = false )
{
    long long v;
    if( value_long_node( parent, name, v, silent ) ); // fine
    else return false; //fail
   
    if( range( v, 0, UINT16_MAX, name, silent ) )
         return false;
    else
    {
        target = v;
        return true; //success
    }
}
bool value_uint32_node( rapidxml::xml_node <> * parent, std::string name, uint32_t & target, bool silent = false )
{
    long long v;
    if( value_long_node( parent, name, v, silent ) ); // fine
    else return false; //fail
   
    if( range( v, 0, UINT32_MAX, name, silent ) )
         return false;
    else
    {
        target = v;
        return true; //success
    }
}
//}

void XML_Parser::load_Images_xml( std::string path, IBank & bank )
{
    LOG( "Loading images from file \"" << path + "\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return;
   
    rapidxml::xml_node <> * root_node;
    root_node = doc.first_node( "images" );
    for( rapidxml::xml_node <>* img_node = root_node->first_node( "image" );
    img_node;
    img_node = img_node->next_sibling( "image" )
    )
    {
        std::string name, img_path;
        if(
        value_string_node( img_node, "name", name ) &&
        value_string_node( img_node, "path", img_path )
        )
        {
            DEB( "LOADED IMG : " << name EL );
            Image * tmp = new Image( bank, name );
            tmp->set_path( img_path );
        }
        else
        {
            ERR( "failed to load Image!" );
        }
    }
}
bool XML_Parser::load_Tile_type_xml( std::string path, Bank < Tile_type >& bank, IBank & images )
{
    path = "xml/tile_type.xml"; //for now just force set it.
    LOG( "Loading from file \"" << path + "\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return false;
   
    rapidxml::xml_node <> * root_node;
    root_node = doc.first_node( "tiles" );
    for( rapidxml::xml_node <>* tile_node = root_node->first_node( "tile" );
    tile_node;
    tile_node = tile_node->next_sibling( "tile" )
    )
    {
        std::string name, img_name;
        int16_t deffense_bonus;
        uint16_t id, movement_cost;
        if(
        value_string_node( tile_node, "name", name ) &&
        value_string_node( tile_node, "img", img_name ) &&
        value_uint16_node( tile_node, "id", id ) &&
        value_int16_node( tile_node, "defense_bonus", deffense_bonus ) &&
        value_uint16_node( tile_node, "movement_cost", movement_cost )
        )
        {
            Image * img = images.get_( img_name );
            if( img == nullptr )
            {
                ERR( "Image : \"" << img_name << "\" not found!" EL );
                continue;
            }
           
            Tile_type & tile_t = *( new Tile_type( img ) );
           
            tile_t.id = id;
            tile_t.name = name;
            tile_t.deffense_bonus = deffense_bonus;
            tile_t.movement_cost = movement_cost;
           
            bank.put_( name, & tile_t );
        }
        else
        {
            ERR( "Failed to read xml data for tile type!" EL );
        }
    }
   
    return true;
}
bool XML_Parser::load_Resource_xml( std::string path, RBank & bank, IBank & images )
{
    path = "xml/resources.xml"; //for now just force set it.
    LOG( "Loading from file \"" << path + "\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return false;
   
   
    rapidxml::xml_node <> * root_node, * test_node;
    root_node = doc.first_node( "resources" );
    for( rapidxml::xml_node <>* res_node = root_node->first_node( "resource" );
    res_node;
    res_node = res_node->next_sibling( "resource" )
    )
    {
        std::string name, img_name;
        uint16_t mass;
        uint32_t value;
        Image * img;
        if( //obligatory values
        value_string_node( res_node, "name", name ) &&
        value_uint32_node( res_node, "value", value )
        )
        { //optional values
            if( !value_uint16_node( res_node, "mass", mass, true ) )
                 mass = 1;
           
            if( !value_string_node( res_node, "img", img_name, true ) )
                 img_name = "unknown";
           
           
            img = images.get_( img_name );
           
            Resource * res;
           
            test_node = res_node->first_node( "weapon" );
            if( test_node != nullptr )
            { //it's weapon!
                Weapon_type * wpn = new Weapon_type( img, name, mass, value );
                res = wpn;
                bank.weapon.put( name, wpn );
            } //continue...
            else
            {
                test_node = res_node->first_node( "armour" );
                if( test_node != nullptr )
                { //it's armour!
                    Armour_type * arm = new Armour_type( img, name, mass, value );
                    res = arm;
                    bank.weapon.put( name, arm );
                } //continue...
                else
                { //it's just resource...
                    res = new Resource( img, name, mass, value );
                }
            }
            bank.put_( name, res );
           
        }
        else
        {
            ERR( "Failed to read xml data for tile type!" EL );
        }
    }
   
   
    return true;
}
bool XML_Parser::load_Building_xml( std::string path, Bank < Building >& bank, IBank & images )
{
    path = "xml/buildings.xml"; //for now just force set it.
    LOG( "Loading from file \"" << path + "\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return false;
   
    return true;
}
bool XML_Parser::load_Settlement_type_xml( std::string path, Bank < Settlement_type >& bank, IBank & images )
{
    path = "xml/settlement_type.xml"; //for now just force set it.
    LOG( "Loading from file \"" << path + "\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return false;
   
    return true;
}


bool XML_Parser::load_map_xml( std::string path, World & world )
{
    LOG( "Loading map from file \"" << path + "map.xml\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return false;
   
    bool success = true;
   
    rapidxml::xml_node <> * map_node, * temp_node;
    map_node = doc.first_node( "map" );
    {
        rapidxml::xml_node <>* dim_node = map_node->first_node( "dimentions" );
        //dimentions (in tiles) of map
        if( dim_node == nullptr )
        {
            ERR( "Couldn't load map. The map have no dimentions! Aborting map loading" EL );
            return false;
        }
        else
        {
            if( !value_uint16_node( dim_node, "width", world.gMap.mWidth ) )
                 success = false;
           
            if( !value_uint16_node( dim_node, "height", world.gMap.mHeight ) )
                 success = false;
           
        }
    }
    {
        rapidxml::xml_node <>* tile_defines_node = map_node->first_node( "tile_defines" );
        if( tile_defines_node == nullptr )
        {
            ERR( "Couldn't load map. No tile defines! Aborting map loading" EL );
            return false;
        }
        else
        {
            //continue..
        }
    }
   
    return true;
}

bool XML_Parser::load_world_xml( std::string path, World & world, IBank & images )
{
    std::cout << std::endl; //separate it from logs above.
    LOG( "Loading world from path : " << path EL );
    if(
    load_Tile_type_xml( path, world.bank_tile_type, images ) &&
    load_Resource_xml( path, world.bank_resource, images ) &&
    load_Settlement_type_xml( path, world.bank_settlement_type, images ) &&
    load_Building_xml( path, world.bank_building, images )
    ); //all ok
    else
    {
        ERR( "Failed to load world assets!" EL );
        return false;
    }
   
    std::ifstream info( path + "info.inf" );
    std::string line;
    while( info.good() )
    {
        getline( info, line );
        if( line.substr( 0, 5 ) == "name:" )
        {
            if( world.name == "" )
            {
                line.erase( 0, 5 );
                world.name = line;
            }
            else
            {
                WAR( "Duplicate parameter \"name:\" world path : " << path EL );
            }
        }
        else if( line.substr( 0, 4 ) == "map:" )
        {
            if( world.map_loaded == false )
            {
                line.erase( 0, 4 );
                if( load_map_xml( path + line, world ) )
                {
                    world.map_loaded = true;
                }
                else
                {
                    WAR( "Fail during map load, world path : " << path EL );
                }
            }
            else
            {
                WAR( "Duplicate parameter \"map:\" world path : " << path EL );
            }
        }
        else if( line.substr( 0, 8 ) == "version:" )
        {
            //check version...
        }
        else if( line == "" )
        {
            //ignore empty lines...
        }
        else
        {
            LOG( "unknown world info parameter : " << line EL );
        }
       
    }
    info.close();
   
    return true; //success
}


void XML_Parser::save_to_file( World *, std::string )
{
    return;
}


ENTER - skip / non empty + enter - debug

LOG  : Loading images from file "xml/images.xml"
DEB  : LOADED IMG : unknown
DEB  : LOADED IMG : button
DEB  : LOADED IMG : main scene
DEB  : LOADED IMG : game background
DEB  : LOADED IMG : pressed
DEB  : LOADED IMG : selected
DEB  : LOADED IMG : TILE_AMBIENT
DEB  : LOADED IMG : TILE_OCEAN
DEB  : LOADED IMG : TILE_GRASSLAND
DEB  : LOADED IMG : TILE_FOREST
LOG  : Image "main scene". Does not have texture, trying to reload one...
LOG  : Image "button". Does not have texture, trying to reload one...

LOG  : Loading world from path : save/world_new_world/
LOG  : Loading from file "xml/tile_type.xml"
WARN ! couldn't find : TILE_DESERT
ERROR! Image : "TILE_DESERT" not found!
LOG  : Loading from file "xml/resources.xml"
LOG  : Loading from file "xml/settlement_type.xml"
LOG  : Loading from file "xml/buildings.xml"
LOG  : Loading map from file "save/world_new_world/map_earth/map.xml"
ERROR! (save/world_new_world/map_earth/) Error: std::bad_alloc
WARN ! Fail during map load, world path : save/world_new_world/
LOG  : Image "game background". Does not have texture, trying to reload one...
DEB  : Deleting window nr : 0
DEB  : Game finished properly...

Process returned 0 (0x0)   execution time : 3.740 s
Press ENTER to continue.


//log dokładnie tego samego programu odpalonego drugi raz...
ENTER - skip / non empty + enter - debug

LOG  : Loading images from file "xml/images.xml"
DEB  : LOADED IMG : unknown
DEB  : LOADED IMG : button
DEB  : LOADED IMG : main scene
DEB  : LOADED IMG : game background
DEB  : LOADED IMG : pressed
DEB  : LOADED IMG : selected
DEB  : LOADED IMG : TILE_AMBIENT
DEB  : LOADED IMG : TILE_OCEAN
DEB  : LOADED IMG : TILE_GRASSLAND
DEB  : LOADED IMG : TILE_FOREST
DEB  : LOADED IMG : TILE_DESERT
LOG  : Image "main scene". Does not have texture, trying to reload one...
LOG  : Image "button". Does not have texture, trying to reload one...

LOG  : Loading world from path : save/world_new_world/
LOG  : Loading from file "xml/tile_type.xml"
LOG  : Loading from file "xml/resources.xml"
LOG  : Loading from file "xml/settlement_type.xml"
LOG  : Loading from file "xml/buildings.xml"
LOG  : Loading map from file "save/world_new_world/map_earth/map.xml"
ERROR! (save/world_new_world/map_earth/) Error: std::bad_alloc
WARN ! Fail during map load, world path : save/world_new_world/
LOG  : Image "game background". Does not have texture, trying to reload one...
DEB  : Deleting window nr : 0
DEB  : Game finished properly...

Process returned 0 (0x0)   execution time : 3.135 s
Press ENTER to continue.


//xml, w czasie wczytywania którego wyskakuje bład. Jest well-formatted według programu XML Copy Editor. Chyba problem nie dotyczy tego pliku...

<?xml version="1.0" encoding="UTF-8"?>

<map>
<dimentions>
<width>17</width>
<height>11</height>
</dimentions>
<tile_defines>
<def>
<name>_</name>
<tile_type>ocean</tile_type>
<elevation>-4</elevation>
<!-- <wild_resources>0</wild_resources> not implemented yet-->
</def>
<def>
<name>.</name>
<tile_type>grassland</tile_type>
<elevation>1</elevation>
</def>
<def>
<name>-</name>
<tile_type>desert</tile_type>
<elevation>1</elevation>
</def>
<def>
<name>M</name>
<tile_type>desert</tile_type>
<elevation>3</elevation> <!-- its hill! -->
</def>
<def>
<name>#</name>
<tile_type>forest</tile_type>
<elevation>1</elevation>
</def>
</tile_defines>


<tile_placement>
___.#.-----.#.___
__#...-M-M-...#__
_.#.#--M_M--#.#._
_#.#_--___--_#.#_
##..M_-----_M..##
..#.___-M-___.#..
##..M_-----_M..##
_#.#_--___--_#.#_
_.#.#--M_M--#.#._
__#...-M-M-...#__
___.#.-----.#.___
</tile_placement>


<player_defines>
<player>
<name>nature</name>
<sign>-</sign>
</player>
<player>
<name>Charlemagne</name>
<sign>C</sign>
<color>
<r>20</r>
<g>0</g>
<b>168</b>
</color>
</player>
<player>
<name>Al-Amin</name> <!-- Muhamed -->
<sign>A</sign>
<color>
<r>32</r>
<g>218</g>
<b>0</b>
</color>
</player>
<player>
<name>Qutlugh</name> <!-- bilge -->
<sign>Q</sign>
<color>
<r>212</r>
<g>84</g>
<b>0</b>
</color>
</player>
<player>
<name>Dezong</name>
<sign>D</sign>
<color>
<r>216</r>
<g>192</g>
<b>0</b>
</color>
</player>
</player_defines>


<owner_placement>
-----------------
--CC---------QQ--
--C-C-------Q-Q--
-C-------------Q-
-C-------------Q-
-----------------
-A-------------D-
-A-------------D-
--A-A-------D-D--
--AA---------DD--
-----------------
</owner_placement>

</map>
P-174524
» 2019-04-26 12:39:50
C/C++
void foo()
{
    rapidxml::xml_document <> doc;
}
Źle. Po wyjściu z foo() zostanie wywołany destruktor obiektu doc, więc wszelkie wskaźniki do węzłów, atrybutów oraz czegokolwiek innego związanego z doc staną się nieaktualne. Przykładowe rozwiązania:
1) Obiekt doc umieszczony gdzieś indziej i istniejący tak długo, jak długo sięgasz do nazw i wartości atrybutów bądź węzłów.
2) Każdorazowe kopiowanie danych po to, aby właścicielem pamięci był wybrany obiekt, a nie doc (który przecież zostanie zniszczony).
3) Używanie » RapidXML » rapidxmlmemory_pool i ręczne zarządzanie pamięcią (nie polecam, chyba że chcesz śrubować wydajność).
P-174525
Temat założony przez niniejszego użytkownika
» 2019-04-26 12:54:25
Wydaje mi się, że dobrze tego używam - używam zawsze tego w takim schemacie

foo()
{
rapidxml::xml_document <> doc;
open(doc,path); //open używa & referencji
//działanie na węzłach doc. W celu wczytania ich do klasy
}

I po funkcji już nie potrzebuję tego doc. Bo wczytałem dane do moich klas.

Chyba, że coś przeoczyłem tutaj?

Ale wczytują mi się wszystkie pozostałe dane - typy pól, obrazki - na obrazki nawet mam dowód, że się wczytały z xmla, bo inaczej nic by mi się nie wyświetliło.
P-174526
» 2019-04-26 13:00:53
I po funkcji już nie potrzebuję tego doc. Bo wczytałem dane do moich klas.
Jak wygląda takie wczytywanie? Bo jeśli tylko bierzesz wskaźnik na
const char *
, to przecież on wskazuje na dane w polu pamięci doc. Rozumiem, że wybierasz drugie rozwiązanie, czyli każdorazowo chcesz kopiować dane. Sama biblioteka daje tylko wskaźniki, musisz samodzielnie skopiować to, co się pod nimi znajduje.

Ale wczytują mi się wszystkie pozostałe dane
Może tak być. Przecież całość nie ginie od razu, po zwolnieniu pamięci kompilator nie wypełnia jej zerami. Dopóki nie nastąpi nowy przydział pamięci w danym miejscu, to poprzednie wartości zmiennych będą dostępne.
P-174527
Temat założony przez niniejszego użytkownika
» 2019-04-26 13:04:37
Przykładowe wczytywanie wygląda tak. Po wyjściu z tej funkcji nie potrzebuję już ani zmiennej doc ani żadnych jej danych. Przepisuję wartości do "prawdziwych" zmiennych posiadanych przez zewnętrzne klasy.
C/C++
bool value_string_node( rapidxml::xml_node <> * parent, std::string name, std::string & target, bool silent = false )
{
   
    rapidxml::xml_node <> * temp_node = parent->first_node( name.c_str() );
   
    if( temp_node == nullptr )
    {
        if( !silent )
             ERR( "Couldn't find " << name << "!" EL );
       
        return false;
    }
    else
    {
        //DEB(name<<" = "<<target EL);
        target = temp_node->value();
        return true;
    }
}

void XML_Parser::load_Images_xml( std::string path, IBank & bank )
{
    LOG( "Loading images from file \"" << path + "\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return;
   
    rapidxml::xml_node <> * root_node;
    root_node = doc.first_node( "images" );
    for( rapidxml::xml_node <>* img_node = root_node->first_node( "image" );
    img_node;
    img_node = img_node->next_sibling( "image" )
    )
    {
        std::string name, img_path;
        if(
        value_string_node( img_node, "name", name ) &&
        value_string_node( img_node, "path", img_path )
        )
        {
            DEB( "LOADED IMG : " << name EL );
            Image * tmp = new Image( bank, name ); // w tym miejscu zmienne wędrują w świat
            tmp->set_path( img_path ); //i też w tym.
        }
        else
        {
            ERR( "failed to load Image!" );
        }
    }
}

ale błąd pochodzi chyba dokładnie stąd :
(przynajmniej tak sugerowałby komunikat który przy błędzie wyskakuje)
C/C++
bool open( rapidxml::xml_document <>& doc, std::string path )
{
    try
    {
        rapidxml::file <> file(( path ).c_str() );
        doc.parse < 0 >( file.data() );
    }
    catch( const std::runtime_error & e )
    {
        ERR( "(" << path << ")" << " Runtime error: " << e.what() EL );
        return false;
    }
    catch( const rapidxml::parse_error & e )
    {
        ERR( "(" << path << ")" << " Parse error: " << e.what() EL );
        return false;
    }
    catch( const std::exception & e )
    { //<-- stąd
        ERR( "(" << path << ")" << " Error: " << e.what() EL );
        return false;
    }
    catch(...)
    {
        ERR( "An unknown error occurred." EL );
        return false;
    }
   
    return true;
}
P-174528
Temat założony przez niniejszego użytkownika
» 2019-04-26 13:14:52
Złapałem błąd! I był głupi.... Jak 99.9% błedów.

zamieniłem

C/C++
bool XML_Parser::load_map_xml( std::string path, World & world )
{
    LOG( "Loading map from file \"" << path + "map.xml\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, path ) )
         return false;
   
    (...)

na

C/C++
bool XML_Parser::load_map_xml( std::string path, World & world )
{
    std::string map_path = path + "map.xml";
    LOG( "Loading map from file \"" << map_path << "\"" EL );
   
    rapidxml::xml_document <> doc;
    if( !open( doc, map_path ) )
         return false;
   
    (...)

próbowałem wczytać xml-a ze ścieżki z folderem
wszystko przez leniwe rozwiązania : /
Poprawię to zaraz, żeby już od początku docierała tu dobra ścieżka...
P-174529
« 1 »
 Strona 1 z 1