Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Allegro 5 file system

This thread is locked; no one can reply to it. rss feed Print
Allegro 5 file system
Ariesnl
Member #2,902
November 2002
avatar

Since the native file dialog doesn't look so nice and also crashes the Windows version of my game. I wanted to make a save/ load screen with a list of slots.
I thought the allegro 5 file system was a nice feature to use for that.
However I saw a "warning" not to use the file's path but the ALLEGRO_FS_ENTRY itself to open the file ? How would I do that ?

Correction.. the Dialog wasn't the problem.. it seems you cannot load a file in windows that was saved in linux for some reason... strange.. looking up the version number just went fine??

- Wisdom is the art of using knowledge
- String theory: There's music in everything

RPG Hacker
Member #12,492
January 2011
avatar

Ariesnl said:

However I saw a "warning" not to use the file's path but the ALLEGRO_FS_ENTRY itself to open the file ? How would I do that

Is this what you're looking for?

Quote:

it seems you cannot load a file in windows that was saved in linux for some reason... strange.. looking up the version number just went fine??

How exactly are you saving the data? Are you just putting a pointer of a struct into a file stream? In that case, yeah, the save file won't be platform-independent. Every compiler has different rules for packing of structs and every architecture has its own endianness. Those and probably some more things influence how the data written to the file will be interpreted when read back. If you want truley platform-independent save files, you'll have to work at a byte level. Only write singular bytes to the file stream and do the conversion (multiple bytes <-> actual data type) yourself. Note that this is a lot slower and you're probably better just having platform-dependent save files, for the sake of the game. In my opinion, sharing save files between different platforms isn't really that important, anyways.

Ariesnl
Member #2,902
November 2002
avatar

Partly it is.. however, trying to be "modern" all my load and save routines use C++ filestream... I'm afraid that won't map to a filepointer system ?
It serializes the current game engine contents and after that the complete universe to an opened filestream.

By the way: g_pUniverse->StoreSector stores the Game engine contents into the current sector.( like the number of ships of a certain kind etc.)

#SelectExpand
1void SaveGame() 2{ 3 ALLEGRO_FILECHOOSER * dlg = NULL; 4 ALLEGRO_PATH * pSavePath = al_get_standard_path(ALLEGRO_RESOURCES_PATH); 5 6 al_append_path_component(pSavePath, "Save"); 7 8 dlg = al_create_native_file_dialog( al_path_cstr(pSavePath,ALLEGRO_NATIVE_PATH_SEP), 9 10 "Save Game...", 11 12 "*.uni", 13 14 ALLEGRO_FILECHOOSER_SAVE); 15 16 if (al_show_native_file_dialog(g_pDisplay,dlg)) 17 18 { 19 20 ofstream SaveFile(al_get_native_file_dialog_path(dlg, 0),ios::out | ios::binary); 21 22 if (SaveFile) 23 24 { 25 26 g_pUniverse->StoreSector(g_pEnterprise->m_nSectorPositionX,g_pEnterprise->m_nSectorPositionY,g_pEngine); 27 28 g_pEngine->Save(SaveFile); 29 30 g_pUniverse->Save(SaveFile); 31 32 SaveFile.close(); 33 34 } 35 36 else 37 38 { 39 40 // handle error 41 42 } 43 44 45 46 } 47 48 49 50 al_destroy_native_file_dialog(dlg); 51 52}

- Wisdom is the art of using knowledge
- String theory: There's music in everything

RPG Hacker
Member #12,492
January 2011
avatar

Ariesnl said:

I'm afraid that won't map to a filepointer system ?

Well, not mapping to a file pointer is kind of the point of Allegro 5's filesystem. It's meant to just provide the data for you and abstract the actual interface so that you don't have to care where the data comes from. It could come from a native file, from a ZIP file or even from a completely different filesystem (like a network filesystem, for example) if you provide the interfaces for that. If you want something that maps to a native file pointer, Allegro's file I/O is probably what you want instead of filesystem (and even then that's probably using platform-dependent C file interfaces under the hood, not C++ interfaces).

As for your save function

#SelectExpand
1g_pUniverse->StoreSector(g_pEnterprise->m_nSectorPositionX,g_pEnterprise->m_nSectorPositionY,g_pEngine); 2g_pEngine->Save(SaveFile); 3g_pUniverse->Save(SaveFile);

These are really the functions that matter. I need to see how exactly you write some of that data to the actual file stream to know what goes wrong.

Ariesnl
Member #2,902
November 2002
avatar

This is what I have now. But it is not very efficient.
and won't work nice with allegro's file system...
sGameObject is a big struct containing everything a unit (sprite) could have...

#SelectExpand
1bool TEngine::Save(ofstream & a_SaveStream) 2{ 3 char szVersion[18]="CURRENT_SECTOR_V1"; 4 a_SaveStream.write((char*)szVersion,sizeof (szVersion)); 5 6 7 std::list<TSprite *>::const_iterator p=m_lstItems.begin(); 8 int m_nObjects = CountSaveObjects(); 9 a_SaveStream.write((char*)&m_nObjects,sizeof (int)); 10 while (p!=m_lstItems.end()) 11 { 12 if ( (!(*p)->m_blDestroyed) && ((*p)->m_ID != ID_NONE) && ((*p)->m_ID != ID_SMOKE)) 13 { 14 sGameObject object; 15 memset(&object, 0, sizeof(sGameObject)); 16 object.m_ID = (*p)->m_ID; 17 object.m_Member = (*p)->m_Member; 18 object.m_dX = (*p)->m_dX; 19 object.m_dY = (*p)->m_dY; 20 object.m_nZ = (*p)->m_nZ; 21 object.m_dAngle = (*p)->m_dAngle; 22 object.m_blCanFind = (*p)->m_blCanFind; 23 object.m_dSpeed = (*p)->m_dSpeed; 24 25 26 if ((*p)->m_ID>ID_BULLET_BOTTOM && (*p)->m_ID<ID_BULLET_TOP) 27 { 28 // sprite is a bullet 29 object.m_nLife = ((TBullet*)(*p))->m_nLife; 30 object.m_nCount = ((TBullet*)(*p))->m_nWait; 31 } 32 else if ((*p)->m_ID>ID_PLANET_BOTTOM && (*p)->m_ID<ID_STAR_TOP) 33 { 34 // sprite is a planet or a star 35 36 } 37 38 if ((*p)->m_ID == ID_EXPLOSION) 39 { 40 // sprite is an explosion 41 object.m_nLife = ((TExplosion*)(*p))->m_nFrame; 42 object.m_nCount = ((TExplosion*)(*p))->m_nWait; 43 } 44 45 if ((*p)->m_ID == ID_SMOKE) 46 { 47 object.m_nLife = ((TSmoke*)(*p))->m_nFrame; 48 object.m_nCount = ((TSmoke*)(*p))->m_nWait; 49 } 50 51 if (((*p)->m_ID>ID_SHIP_BOTTOM)&&((*p)->m_ID<ID_BASE_TOP)) 52 { 53 //sprite is a ship or a starbase 54 55 object.m_blDocked = ((TShip *)(*p))->m_blDocked; 56 object.m_blDocking = ((TShip *)(*p))->m_blDocking; 57 object.m_blReleasing = ((TShip *)(*p))->m_blReleasing; 58 object.m_blMustBeDestroyed = ((TShip *)(*p))->m_blMustBeDestroyed; 59 object.m_blMustSurvive = ((TShip *)(*p))->m_blMustSurvive; 60 61 object.m_blMustReachPosition = ((TShip *)(*p))->m_blMustReachPosition; 62 object.m_blDock = ((TShip *)(*p))->m_blDock; 63 object.m_blNoRelease = ((TShip *)(*p))->m_blNoRelease; 64 object.m_blCanFind = ((TShip *)(*p))->m_blCanFind; 65 object.m_dWaypointX = ((TShip *)(*p))->m_dWaypointX; 66 67 object.m_dWaypointY = ((TShip *)(*p))->m_dWaypointY; 68 object.m_nTorpedoes = ((TShip *)(*p))->m_nTorpedoes; 69 object.m_nRepairItem = ((TShip *)(*p))->m_nRepairItem; 70 object.m_nCrew = ((TShip *)(*p))->m_nCrew; 71 object.m_nEnergy = ((TShip *)(*p))->m_nEnergy; 72 73 object.m_nShieldEnergy = ((TShip *)(*p))->m_nShieldEnergy; 74 object.m_nPhaserEnergy = ((TShip *)(*p))->m_nPhaserEnergy; 75 object.m_nPreferedTarget = ((TShip *)(*p))->m_nPreferedTarget; 76 object.m_nPhaserPower = ((TShip *)(*p))->m_nPhaserPower; 77 object.m_blCanCloak = ((TShip *)(*p))->m_blCanCloak; 78 79 object.m_blCanFind = ((TShip *)(*p))->m_blCanFind; 80 object.m_blPhaserOn = ((TShip *)(*p))->m_blPhaserOn; 81 object.m_nCloakCharge = ((TShip *)(*p))->m_nCloakCharge; 82 object.m_nCloakCounter = ((TShip *)(*p))->m_nCloakCounter; 83 object.m_nPhotonTimer = ((TShip *)(*p))->m_nPhotonTimer; 84 85 object.m_nTranslucency = ((TShip *)(*p))->m_nTranslucency; 86 object.m_nTask = ((TShip *)(*p))->m_nTask; 87 object.m_nCloakState = ((TShip *)(*p))->m_nCloakState; 88 89 // store health 90 for (size_t i=0; i< ((TShip *)(*p))->m_lstHealth.size();i++) 91 { 92 object.m_nHealth[(HEALTH)i]=((TShip *)(*p))->m_lstHealth[(HEALTH)i]; 93 } 94 95 if ((*p)->m_ID==ID_PLAYER) 96 { 97 // player specific ! sprite is the player 98 object.m_nSectorPositionX = ((TEnterprise *) (*p))->m_nSectorPositionX; 99 object.m_nSectorPositionY = ((TEnterprise *) (*p))->m_nSectorPositionY; 100 object.m_nWarpFactor = ((TEnterprise *) (*p))->m_nWarpFactor; 101 object.m_nProbes = ((TEnterprise *) (*p))->m_nProbes; 102 } 103 } 104 105 a_SaveStream.write((char*)&object,sizeof (sGameObject)); 106 } 107 ++p; 108 } 109 return true; 110} 111 112 113 114 115 116 117 118bool TEngine::Load(ifstream & a_LoadStream) 119{ 120 Clear(false); 121 char szVersion[18]; 122 a_LoadStream.read((char *)szVersion,sizeof(char)*18); 123 124 int m_nObjects; 125 a_LoadStream.read((char *)&m_nObjects,sizeof(int)); 126 127 for (int i=0;i<m_nObjects;i++) 128 { 129 sGameObject object; 130 memset(&object, 0, sizeof(sGameObject)); 131 132 a_LoadStream.read((char *)&object,sizeof(object)); 133 TSprite * pSprite=NULL; 134 135 if ((object.m_ID>=ID_CLASS_A)&&(object.m_ID<ID_BLACK_HOLE)) 136 { 137 pSprite = new TSpaceObject(object.m_ID); 138 139 } 140 if (object.m_ID == ID_PROBE) 141 { 142 pSprite = new TProbe(object.m_dX , 143 object.m_dY, 144 object.m_dSpeed, 145 object.m_dAngle, 146 object.m_nZ,g_pEnterprise); 147 } 148 else if ((object.m_ID>ID_BULLET_BOTTOM)&&(object.m_ID<ID_BULLET_TOP)) 149 { 150 pSprite = new TBullet( object.m_dX , 151 object.m_dY, 152 object.m_dSpeed, 153 object.m_dAngle, 154 object.m_nZ, 155 object.m_ID, 156 object.m_Member); 157 158 ((TBullet *)pSprite)->m_nLife = object.m_nLife; 159 ((TBullet *)pSprite)->m_nWait = object.m_nCount; 160 } 161 else if (object.m_ID == ID_EXPLOSION) 162 { 163 pSprite = new TExplosion(object.m_dX, 164 object.m_dY, 165 object.m_dSpeed, 166 object.m_dAngle, 167 object.m_nZ); 168 169 170 } 171 else if (object.m_ID>ID_SHIP_BOTTOM) 172 { 173 switch (object.m_ID) 174 { 175 case ID_PLAYER: 176 { 177 pSprite = new TEnterprise(); 178 g_pEnterprise = (TEnterprise *)pSprite; 179 g_pEnterprise->m_nSectorPositionX = object.m_nSectorPositionX; 180 g_pEnterprise->m_nSectorPositionY = object.m_nSectorPositionY; 181 g_pEnterprise->m_nWarpFactor = object.m_nWarpFactor; 182 g_pEnterprise->m_nProbes = object.m_nProbes; 183 } 184 185 186 break; 187 188 case ID_GALAXYCLASS: 189 pSprite = new TFederation_Ship(); 190 191 break; 192 193 case ID_KLINGONBC: 194 { 195 pSprite = new TKlingonBC(); 196 197 198 199 } 200 break; 201 202 case ID_KLINGONBOP: 203 { 204 pSprite = new TKlingonBOP(); 205 } 206 207 break; 208 209 case ID_ROMULANBOP: 210 { 211 pSprite = new TRomulanBop(); 212 213 214 215 } 216 break; 217 218 case ID_FEDERATIONBASE: 219 { 220 pSprite = new TStarbase(ID_FEDERATIONBASE); 221 222 } 223 break; 224 225 case ID_ROMULAN_BASE: 226 { 227 pSprite = new TStarbase(ID_ROMULAN_BASE); 228 229 } 230 break; 231 232 233 default: 234 throw A5Exception("Unknown shiptype"); 235 break; 236 237 238 } 239 240 if (pSprite !=NULL) 241 { 242 // save TShip specific 243 ((TShip *)pSprite)->m_nCrew = object.m_nCrew; 244 ((TShip *)pSprite)->m_nRepairItem = object.m_nRepairItem; 245 ((TShip *)pSprite)->m_nTorpedoes = object.m_nTorpedoes; 246 ((TShip *)pSprite)->m_nShieldEnergy = object.m_nShieldEnergy; 247 ((TShip *)pSprite)->m_nPhaserEnergy = object.m_nPhaserEnergy; 248 249 ((TShip *)pSprite)->m_nPreferedTarget = object.m_nPreferedTarget; 250 ((TShip *)pSprite)->m_nPhaserPower = object.m_nPhaserPower; 251 ((TShip *)pSprite)->m_dWaypointX = object.m_dWaypointX; 252 ((TShip *)pSprite)->m_dWaypointX = object.m_dWaypointY; 253 ((TShip *)pSprite)->m_blDocked = object.m_blDocked; 254 255 ((TShip *)pSprite)->m_blDocking = object.m_blDocking; 256 ((TShip *)pSprite)->m_blReleasing = object.m_blReleasing; 257 ((TShip *)pSprite)->m_blMustBeDestroyed = object.m_blMustBeDestroyed; 258 ((TShip *)pSprite)->m_blMustSurvive = object.m_blMustSurvive; 259 ((TShip *)pSprite)->m_blMustReachPosition = object.m_blMustReachPosition; 260 261 ((TShip *)pSprite)->m_blNoRelease = object.m_blNoRelease; 262 ((TShip *)pSprite)->m_blCanFind = object.m_blCanFind; 263 ((TShip *)pSprite)->m_blCanCloak = object.m_blCanCloak; 264 ((TShip *)pSprite)->m_blCanFind = object.m_blCanFind; 265 ((TShip *)pSprite)->m_blPhaserOn = object.m_blPhaserOn; 266 267 ((TShip *)pSprite)->m_nCloakCharge = object.m_nCloakCharge; 268 ((TShip *)pSprite)->m_nCloakCounter = object.m_nCloakCounter; 269 ((TShip *)pSprite)->m_nPhotonTimer = object.m_nPhotonTimer; 270 ((TShip *)pSprite)->m_nTranslucency = object.m_nTranslucency; 271 ((TShip *)pSprite)->m_nTask = object.m_nTask; 272 273 ((TShip *)pSprite)->m_nCloakState = object.m_nCloakState; 274 275 for (size_t i=0;i<((TShip *)pSprite)->m_lstHealth.size();i++) 276 { 277 ((TShip *)pSprite)->m_lstHealth[i]=object.m_nHealth[i]; 278 } 279 } 280 } 281 282 if (pSprite !=NULL) 283 { 284 // save TSprite specific 285 pSprite->m_dX = object.m_dX; 286 pSprite->m_dY = object.m_dY; 287 pSprite->m_nZ = object.m_nZ; 288 pSprite->m_dSpeed = object.m_dSpeed; 289 pSprite->m_dAngle = object.m_dAngle; 290 pSprite->m_Member = object.m_Member; 291 pSprite->m_ID = object.m_ID; 292 Add(pSprite); 293 } 294 295 } 296 297std::list<TSprite *>::const_iterator p=m_lstItems.begin(); 298while (p!=m_lstItems.end()) 299{ 300 if (((*p)->m_ID>ID_SHIP_BOTTOM)&&(((TShip *)(*p))->m_blDocked)) 301 { 302 ((TShip *)(*p))->m_blCanFind = false; 303 ((TShip *)(*p))->m_blDocking=false; 304 ((TShip *)(*p))->m_blReleasing = false; 305 ((TShip *)(*p))->m_dSpeed=0; 306 ((TShip *)(*p))->m_pBaseTarget=(TShip *) Seekstarbase(((TShip *)(*p))->m_Member,false,100000,((TShip *)(*p))->m_dX,((TShip *)(*p))->m_dY); 307 ((TShip *)(*p))->m_pTarget = ((TShip *)(*p))->m_pBaseTarget; 308 } 309 else if (((*p)->m_ID>ID_SHIP_BOTTOM)&&(((TShip *)(*p))->m_blDocking)) 310 { 311 ((TShip *)(*p))->m_blDocking=true; 312 ((TShip *)(*p))->m_blReleasing = false; 313 ((TShip *)(*p))->m_pBaseTarget=(TShip *) Seekstarbase(((TShip *)(*p))->m_Member,false,100000,((TShip *)(*p))->m_dX,((TShip *)(*p))->m_dY); 314 ((TShip *)(*p))->m_pTarget = ((TShip *)(*p))->m_pBaseTarget; 315 } 316 317 ++p; 318} 319 320 321return true; 322 323}

- Wisdom is the art of using knowledge
- String theory: There's music in everything

Audric
Member #907
January 2001

If you want cross-platform behavior, you need to save your structures one field at a time. Choose an endianness for the file format, and save every multi-byte integer with the specific function for its size (short : 16, long: 32).

al_fwrite16le(saveFile, object.m_dX);
al_fwrite16le(saveFile, object.m_dY);

Same when you reload them :

object.m_dX = al_fread16le(saveFile);
object.m_dY = al_fread16le(saveFile);

Go to: