Propdump

From ActiveWiki
Revision as of 17:26, 15 March 2021 by Chris (talk | contribs) (→‎Read Property Format)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

A propdump is a file that contains the properties of the world. It can be used to backup and restore a worlds objects. Together with the atdump (world attributes) and elevdump (world terrain) it is a complete backup of a world.

Propdump structure

Every line in a propdump file is an object. That line contains all data about the object. The first 13 numbers are to be seperated by spaces and tell things about positioning and more, the last part that remains contains non numeric data and tells us about the object name, description and action.

Example of line:

359971 1167343043 -800 0 200 0 0 0 0 7 0 99 0 floor01create name gz1wat1, texture water1_top mask=semitrans10,solid no,move 0 0.1 0.5 time=2 smooth sync
  • The 1st part (358257) is the citizen number of the owner of the object.
  • The 2nd part (1167343043) is the timestamp that contains the date when the object had been created.
  • The 3rd part (-800) is the X position of the object.
  • The 4th part (0) is the Y position of the object.
  • The 5th part (200) is the Z position of the object.
  • The 6th part (0) is the YAW orientation of the object.
  • The 7th part (0) is the Tilt orientation of the object.
  • The 8th part (0) is the Roll orientation of the object.
  • The 9th part (0) is the type of the object. (0: Object. 1: Camera. 2: Zone. 3: Particle Emitter. 4: Mover.).
  • The 10th part (7) is the length of the model name (floor01).
  • The 11th part (0) is the length of the description.
  • The 12th part (99) is the length of the action (create name gz1wat1 [...]).
  • The 13th part (0) is the length of the object data (for object types other than 0).
  • The 14th part (all that remains) contains object information in the length described above.

Newline, or carriage return with line feed \r\n, is replaced with characters 0x80 0x7F (before 3.3: 0x0D 0x7F), and \n with character 0x7F. A solitary \r is not translated.


Code Sniplets

Write Property Format

//
// Helper function to convert NL characters in strings
//
static void codeNL(char* string)
{
    while (*string)
    {
        if (*string == '\r' && *(string + 1) == '\n')
        {
            // Note, if 128 is without 127, it would be the euro-sign
            *string = (char)128;
            string++;
            *string = (char)127;
        }
        else if (*string == '\n')
            *string = (char)127;
        string++;
    }
}

//
// on AW_EVENT_CELL_OBJECT from aw_query() or aw_cell_next()
//
void handle_cell_object(void)
{
    char* buf             = NULL;
    char* model           = NULL;
    char* description     = NULL;
    char* action          = NULL;
    char* data_hex        = NULL;
    int   model_len       = 0;
    int   description_len = 0;
    int   action_len      = 0;
    int   data_len        = 0;
    int   i               = 0;

#if !defined(UNICODE)
    model       = aw_string(AW_OBJECT_MODEL);
    description = aw_string(AW_OBJECT_DESCRIPTION);
    action      = aw_string(AW_OBJECT_ACTION);
#else
    model       = aw_string_from_unicode(aw_stringW(AW_OBJECT_MODEL));
    description = aw_string_from_unicode(aw_stringW(AW_OBJECT_DESCRIPTION));
    action      = aw_string_from_unicode(aw_stringW(AW_OBJECT_ACTION));
#endif
    model_len       = (int)strlen(model);
    description_len = (int)strlen(description); 
    action_len      = (int)strlen(action);

    if (aw_int(AW_OBJECT_TYPE))
    {
        unsigned char* data = (unsigned char*)aw_data(AW_OBJECT_DATA, &data_len);
        if (data_len < 0 || data_len > 4096)
            return;
        data_hex = (char*)calloc(1, (data_len * 2) + 1);
        // binary to hex
        for (i = 0; i < data_len && data; i++)
            sprintf(&data_hex[i * 2], "%02x", data[i]);
    }
    else
    {
        if (model_len <= 0)
            return; // mandatory
        data_hex = (char*)calloc(1, 1);
    }

    buf = (char*)calloc(1, (9 * 11) + 9 +
                        model_len + description_len + action_len +
                        (data_len * 2) + 1);

    sprintf(buf, "%d %d %d %d %d %d %d %d %d %d %d %d %d %s%s%s%s",
        aw_int(AW_OBJECT_OWNER), aw_int(AW_OBJECT_BUILD_TIMESTAMP),
        aw_int(AW_OBJECT_X), aw_int(AW_OBJECT_Y), aw_int(AW_OBJECT_Z),
        aw_int(AW_OBJECT_YAW), aw_int(AW_OBJECT_TILT), aw_int(AW_OBJECT_ROLL),
        aw_int(AW_OBJECT_TYPE),
        model_len, description_len, action_len, data_len,
        model, description, action, data_hex);

     codeNL(buf);

    // TODO: write buf to file,
    // append NL ('\n') as record delimiter, or use fputs()
    ...

   free(data_hex);
   free(buf);
}

Read Property Format

// global static file pointer
// open the property file, i.e "propdump.txt"
// see propload() below
static FILE* fp = NULL;

//
// Helper function to convert NL characters in strings
//
static void decodeNL(int bytes, char* string)
{
    while (bytes--)
    {
        // Note, if 128 is without 127, it's the euro-sign
        if ((unsigned char)*string == 128 && (unsigned char)*(string + 1) == 127)
        {
            *string = '\r';
            string++;
            *string = '\n';
        }
        else if ((unsigned char)*string == 127)
            *string = '\n';
        string++;
    }
}

//
// load one object from a property-file into the world
//
static int load_object(void)
{
    int           rc              = 0;
    int           owner           = 0;
    int           build_time      = 0;
    int           x               = 0;
    int           y               = 0;
    int           z               = 0;
    int           yaw             = 0;
    int           tilt            = 0;
    int           roll            = 0;
    int           model_len       = 0;
    int           description_len = 0;
    int           action_len      = 0;
    char          string[2048]    = { 0 };
    char          buf[8192]       = { 0 };

    int           i               = 0;
    int           byte            = 0;
    int           object_type     = 0;
    int           data_len        = 0;
    char          data[4096];
    char* bytes                   = NULL;


    if (!fp)
        return -2;

    object_type     = 0;
    model_len       = 0;
    description_len = 0;
    action_len      = 0;
    data_len        = 0;

    rc = fscanf(fp, "%d %d %d %d %d %d %d %d %d %d %d %d %d %[^\n]", &owner,
                &build_time, &x, &y, &z, &yaw, &tilt, &roll, &object_type, &model_len,
                &description_len, &action_len, &data_len, buf);

    if (rc == EOF)
        return -1;

    while (yaw < 0)
        yaw += 3600;
    while (yaw >= 3600)
        yaw -= 3600;

    decodeNL(object_len + description_len + action_len, buf);

    aw_int_set(AW_OBJECT_X, x);
    aw_int_set(AW_OBJECT_Y, y);
    aw_int_set(AW_OBJECT_Z, z);
    aw_int_set(AW_OBJECT_YAW, yaw);
    aw_int_set(AW_OBJECT_TILT, tilt);
    aw_int_set(AW_OBJECT_ROLL, roll);
    aw_int_set(AW_OBJECT_OWNER, owner);
    aw_int_set(AW_OBJECT_BUILD_TIMESTAMP, build_time);

    memcpy(string, buf, model_len);
    string[model_len] = '\0';
#if !defined(UNICODE)
    aw_string_set(AW_OBJECT_MODEL, string);
#else
    aw_string_setW(AW_OBJECT_MODEL, aw_string_to_unicode(string));
#endif

    memcpy(string, buf + object_len, description_len);
    string[description_len] = '\0';
#if !defined(UNICODE)
    aw_string_set(AW_OBJECT_DESCRIPTION, string);
#else
    aw_string_setW(AW_OBJECT_DESCRIPTION, aw_string_to_unicode(string));
#endif
    
    memcpy(string, buf + object_len + description_len, action_len);
    string[action_len] = '\0';
#if !defined(UNICODE)
    aw_string_set(AW_OBJECT_ACTION, string);
#else
    aw_string_setW(AW_OBJECT_ACTION, aw_string_to_unicode(string));
#endif

    if (object_type == AW_OBJECT_TYPE_UNKNOWN)
    {
        // skip this object
        return 0x70000001;
    }
    if (object_type < 0)
    {
        // skip this object
        return 0x70000002;
    }
    if (object_type &&
        strlen(buf) > (size_t)(object_len + description_len + action_len))
    {
        if ((data_len < 0 || data_len > 4096))
        {
            // skip this object
            return 0x70000003;
        }
        memset(data, 0, sizeof(data));
        sscanf(&buf[object_len + description_len + action_len], "%s", data);
        if (strlen(data) != (size_t)data_len * 2)
        {
            // skip this object
            return 0x70000004;
        }
        bytes  = (char*)calloc(1, data_len);
        for (i = 0; i < data_len; i++)
        {
            string[0] = data[i * 2];
            string[1] = data[(i * 2) + 1];
            string[2] = 0x0;
            byte      = 0;
            sscanf(string, "%02x", &byte);
            bytes[i] = (unsigned char)byte;
        }
        aw_int_set(AW_OBJECT_TYPE, object_type);
        aw_data_set(AW_OBJECT_DATA, bytes, data_len);
        free(bytes);
    }
    else
    {
        if (model_len <= 0)
        {
            // skip this object
            return 0x70000005;
        }
        aw_int_set(AW_OBJECT_TYPE, 0);
        aw_data_set(AW_OBJECT_DATA, NULL, 0);
    }

    if ((rc = aw_object_load()))
    {
        // an error loading this object to the world occurred
        return rc;
    }

    return 0;
}

int propload()
{
    int rc = 0;

    if (fp)
        // already open
        return -1001;

    fp = fopen("propdump.txt", "r");
    if (!fp)
        // failed to open
        return -1002;
    if (fscanf(fp, "propdump version %d", &version) != 1 || version != 4)
    {
        // wrong format header
        rc = -1003;
        goto onFail;
    }

    // delete world's property content
    aw_callback_set(AW_CALLBACK_DELETE_ALL_OBJECTS_RESULT, NULL);
    if (rc = aw_delete_all_objects())
    {
        // failed to delete world's property objects
        goto onFail;
    }

    aw_callback_set(AW_CALLBACK_OBJECT_RESULT, NULL);
    do
    {
        rc = load_object();
        if (rc > 0x70000000 && rc < 0x70000006)
        {
            // an invalid line, can skip it, but inform user!
            rc = 0;
        }
    } while (rc == 0);

    if (rc == -1) // EOF
        rc = 0;

onFail:
    fclose(fp);
    fp = NULL;
    return rc;
}

See Also