Re: Comming Code for Binary

From: Patrick Dughi (dughi@imaxx.net)
Date: 06/05/00


        > other stuff snipped out <
>
> Please understand that this project temporarily is going forward with the
> understanding that a pfile wipe will be needed.  This project is large
> enough and I do not have time to get into how to create a pfile converter.
>
        Believe it or not, writing a binary pfile converter is a snap.

                                Really!



        As far as the computer is concerned, a binary player file is just
a big stream of contigious bytes.  When you're reading in your pre-binary
file, you simply do a fread() and read in the number of bytes that the
sizeof(struct char_file_u) returns.  Your awaiting empty structure is then
filled in, byte per byte, to the bottom.

        Lets say that you add something small, like an 'int' value.  Let's
also make life simple and say you only have one player.  You've got an
extra sizeof(int) to allocate [1].  How do we get that extra bit in?

        Well, we know that we don't want to write a line of code to copy
over each single value.  We want to make life easy.  Easy it is, too -
when you realize that if you add the int at the bottom of the structure
definition, you can read in the entire pfile, and then add the int at the
end:

  struct old_struct {
     ..stuff here...
  };

        to ->

  struct new_struct {
     ..stuff here...
     int my_new_int;
  };

        "How do I do that?" you may ask.

        Simple enough, lets take our char_file_u structure, and leave it
there.  We need to read in that many bytes so it should 'keep' for
reference.  Let's make an exact copy of it, and add an int to the end
- we'll call it 'char_file_new'. Now, we want to load in our old
character into a previously allocated char_file_new structure;

        fread(&our_char_file_new,sizeof(char_file_u),1,old_player_file);

        Disecting that a bit, we see as the second argument, we're reading
in only the number of bytes in the old structure - but we're reading it
into the new structure.  That leaves us with an int of allocated, but
potentially uninitialized data (though calloc does set it to 0). So, init
it and then write out a new file:

        our_char_file_new.my_new_int = 0;

        fwrite(&our_char_file_new, sizeof(char_file_new),1,new_player_file);

        At the end, you'll want to copy your new char file over by hand,
and remove the old char_file_u structure and replace it with the new one.
Then, after bootup, you're ready to go.

        The fun thing is that if you stick all that code into a
while(!feof()) loop, it will do your entire pfile, regardless of the
size[2].  Of course, that's not the best part - as long as you stick to
adding to the end only, this technique ought to work every time for
anything you put in there.  Lets see this full code in action, via Mailer
Code (ie, untested) - we'll assume we're keeping it in the util dir
though.

---begin code---
#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"

struct char_file_u_new {
   /* char_player_data */
   char name[MAX_NAME_LENGTH+1];
   char description[EXDSCR_LENGTH];
   char title[MAX_TITLE_LENGTH+1];
   byte sex;
   byte chclass;
   byte level;
   sh_int hometown;
   time_t birth;   /* Time of birth of character     */
   int  played;    /* Number of secs played in total */
   ubyte weight;
   ubyte height;

   char pwd[MAX_PWD_LENGTH+1];    /* character's password */

   struct char_special_data_saved char_specials_saved;
   struct player_special_data_saved player_specials_saved;
   struct char_ability_data abilities;
   struct char_point_data points;
   struct affected_type affected[MAX_AFFECT];

   time_t last_logon;           /* Time (in secs) of last logon */
   char host[HOST_LENGTH+1];    /* host of last logon */
   int my_new_int;
};

void main(int argc, char *argv[]) {
  FILE *old_p;
  FILE *new_p;
  int size, recs;

  struct char_file_u_new new_u;

  if (argc != 2) {
    printf("Usage: %s playerfile-name\n", argv[0]);
    exit();
  }

  if (!(old_p = fopen(argv[1], "r+"))) {
    printf("Can't open %s.", argv[1]);
    exit();
  }

  new_p = fopen("players.new", "w");

  /* this part is duplicated in the stock db.c player load */

  fseek(old_p, 0L, SEEK_END);
  size = ftell(old_p);
  rewind(old_pl);
  recs = size / sizeof(struct char_file_u);
  if (size % sizeof(struct char_file_u)) {
    fprintf(stderr, "Bad record count for oldfile.\n");
    exit(1);
  }
  printf("Converting.\n");

  for (size = 0; size < recs; size++) {
    fread(&new_u, sizeof(struct char_file_u), 1, old_p);
    new_u.my_new_int=0;
    fwrite(&new_u, sizeof(struct char_file_u_new), 1, new_p);
  }
  printf("old: %d   new: %d  number of records: %d\n",
        sizeof(struct char_file_u), sizeof(struct char_file_u_new),
        recs);

  printf("All done.\n");

  fclose(old_p);
  fclose(new_p);
}



-- end code ---

        That didn't take but 5 minutes to type up, and the actual part
that does anything is squished between that for loop - it's only 3 lines
long!

        It's a bit more complicated to remove/change size of entries in
char_file_u (like changing the title length, etc) - but it's done in
nearly the same way...though you would use strcpy or memmove to move
chunks over and manually deal with the altered portions, truthfully, it's
no more difficult than the above.

        So, go ahead, write your own pfile converter, test it, and in the
future you can make those innovative changes you want without worrying
about player wipes.

                                                        PjD

[1] - Technically there's more to it.  Depending on your system,
byte padding may be inserted at the front, or rear of a structure/variable
inorder to insure word-aligned boundries in memory.  The trick is just to
always write a new struct with everything you want - and not to simply
allocate +sizeof(int) more memory.

[2] - assuming you correctly open the file and yada-yada-yada all the
other stuff that it's simply assumed you're going to do when you read from
one file and write to another.


     +------------------------------------------------------------+
     | Ensure that you have read the CircleMUD Mailing List FAQ:  |
     |  http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html  |
     +------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 04/10/01 PDT