Data Management

Posts: 15
Joined: 2012.03
Post: #1
Hi everyone,

I'd like to be able to save data (consisting of either a large C struct or an array of C structs) to my computer's desktop from my cocoa editor program and then drag that file over to my iOS app's resource bundle. I find that with certain types of data this works but for other types it doesn't - I tried using both fread()/fwrite() as well as NSData and the results remained the same. Is this the result of some difference between architectures?
Quote this message in a reply
Posts: 5,143
Joined: 2002.04
Post: #2
That's not very safe, but since ARM and x86 are both little-endian, it can be made to work with some care. I'd suggest adding assertions that sizeof(yourStruct) == whatYouExpect and offsetof(yourStruct, eachField) == whatYouExpect. Sticking to the [u]int[n]_t types from <stdint.h> + float/double and sorting the fields of your structs by size, largest first, will help too.

For maximum safety, serialize each primitive explicitly yourself.
Quote this message in a reply
Posts: 15
Joined: 2012.03
Post: #3
Thanks for the reply. So here's just one data struct from my game (which is by the way an open world style rpg so lots of data)....

typedef struct
    rain_particle_t g_rain[MAX_RAIN_PARTICLES];
    snow_particle_t g_snow[MAX_SNOW_PARTICLES];
    time_interval_t g_weather_start_turn;
    time_interval_t g_weather_end_turn;
    time_interval_t g_weather_plateu_start_turn;
    time_interval_t g_weather_plateu_end_turn;
    float g_weather_intensity;
    float g_curr_weather_intensity;
    BOOL g_is_snow_active;
    BOOL g_is_rain_active;
    int g_particle_cnt;
    BOOL g_is_lightning;
    int lightning_timer;
    cmbt_msg_t cmbt_msgs[MAX_CMBT_MSGS];
    int     cmbt_is_active;

    int     cmbt_player_ap;
    int     cmbt_bot_ap;
    int     cmbt_foe_idx_list[MAX_CMBT_FOES];
    int     cmbt_ally_idx_list[MAX_CMBT_ALLIES];
    int     cmbt_foe_cnt;
    int     cmbt_ally_cnt;
    int     cmbt_turn_type;
    int     cmbt_turn_idx;
    int     cmbt_end_turn_at_end_delay;
    int     cmbt_did_update_player_spell_ai;
    int     player_target_type;
    int     player_target_idx;
    int     player_readied_spell;
    float   player_target_alpha;
    BOOL    is_player_target_darkening;

    int             key_value_count;
    time_interval_t    tick_cnt;
    time_interval_t turn_cnt;

    player_t            player;
        object_t            objects[MAX_OBJECTS];
    container_t         containers[MAX_CONTAINERS];
    door_t              doors[MAX_DOORS];
    bot_t             enemies[MAX_BOTS];
    //bot_t               npcs[MAX_NPCS];
    item_t              items[MAX_ITEMS];
    item_t              item_quick_slot[MAX_ITEM_QUICK_SLOTS];
    light_t             light_list[LIGHT_LIST_SIZE];
        decal_t    blood_decal_list[MAX_BLOOD_DECALS];    
        spell_effect_t      spell_effects[MAX_WORLD_SPELL_EFFECTS];
        shop_t              shops[MAX_SHOPS];
    int npc_cnt;
    int container_cnt;
    int item_cnt;
    int portal_cnt;
    int door_cnt;
    int light_cnt;
    int blood_decal_cnt;
    int uniqueid_cntr;
    int spell_quick_slots[MAX_SPELL_QUICK_SLOTS];
    time_interval_t game_delay;

...From what your telling me this was poor planning on my part Cry
and trying to do what you describe to all of my data seems unrealistic especially considering that I'm not a great programmer and have little experience with really low-level C stuff.

Is there any way out of or around this without having to parse every single struct?

This is a shot in the dark, but might it be possible to reorder all of the structs' fields using bit fields in order to eliminate paddings?
Quote this message in a reply
Posts: 5,143
Joined: 2002.04
Post: #4
bitfields will reduce portability even further.

There's nothing ridiculously bad about this struct. Replace "int" with "int32_t" (or smaller if you like); replace BOOL with uint8_t; ensure time_interval_t is a typedef off an explicitly sized type, and sort the fields big to small and it should start working... (and of course do the same to the nested struct types).

I really do suggest adding at least the sizeof() assertion though. Better to crash at startup when the assertion fails than corrupt memory or data later.

Doing this the right way is easy too, but verbose:

void serialize_game_state(stream_t stream, game_state_t const *state)
    for (int i = 0; i < RAIN_PARTICLE_COUNT; ++i) serialize_rain_particle(stream, &(state->rain_particles[i]));
    serialize_float(stream, g_weather_density);
    serialize_int(stream, ...

and similarly for deserialize. Then you just have to implement serialize_float, serialize_int, etc. portably.
Quote this message in a reply
Posts: 15
Joined: 2012.03
Post: #5
Thanks OneSadCookie for all of your help.

So for simplicity's sake let's just say that I'm only saving game data from the OS app into the iOS app's docs directory. Should I be concerned about the future portability of this data (i.e. if apple changes the arm architecture in some way that somehow affects the memory mapping or whatever)? ...Or are there other concerns I'm missing? Also, how likely a scenario (the architectual change) do you personally think this is? I'm not adverse to gambling if I feel fairly certain (say 80%+) of its unlikelihood.

Quote this message in a reply
Posts: 5,143
Joined: 2002.04
Post: #6
You will always be able to write code to read whatever format you decide on now. Just be aware that taking the shortcut now does not necessarily mean you will get away without writing the hard code forever.

Also, always write a few sanity bytes and a version number at the start of each of your custom file formats. Makes it easy to change the format in the future, and to read multiple versions of the format.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Game State Management Mattonaise 6 9,491 Apr 16, 2011 10:18 AM
Last Post: Mattonaise
  iPhone memory management mlady 2 4,158 Mar 10, 2009 03:10 PM
Last Post: mlady