/* * Parse a <properties> node into a list of property objects. */ static GHashTable *parse_properties(xmlNode *node) { GHashTable *props = g_hash_table_new_full(&g_str_hash, &g_str_equal, &g_free, &g_free); xmlNode *properties_node = get_first_child_for_name(node, "properties"); if (!properties_node) { return props; } GSList *properties_list = get_children_for_name(properties_node, "property"); GSList *property_item = properties_list; while (property_item) { xmlNode *property_node = (xmlNode*)property_item->data; property_item = g_slist_next(property_item); char *name = g_strdup(get_xml_attribute(property_node, "name")); char *value = get_xml_attribute(property_node, "value"); if (!value) { value = (char*)xmlNodeGetContent(property_node); } value = g_strdup(value); g_hash_table_insert(props, name, value); } g_slist_free(properties_list); return props; }
static inline float * get_float_points (xmlNode *node, const char *name, int *num_points) { assert (node); assert (name); assert (num_points); char *attr = get_xml_attribute (node, name); if (attr) { int i = 0; float *points = NULL; LIST *nums_str = split (attr, " ,"); if (!_al_list_is_empty (nums_str)) { *num_points = _al_list_size (nums_str) / 2; points = al_malloc (*num_points * 2 * sizeof (float)); LIST_ITEM *item = _al_list_front (nums_str); while (item) { points[i++] = atof ((char *)_al_list_item_data (item)); item = _al_list_next (nums_str, item); } } _al_list_destroy (nums_str); return points; } return NULL; }
LOCAL_EXTERN int get_action_info_by_name(const gchar *key, const gchar **names, const gchar **values, gchar **name, UI_CALLBACK *info) { static const struct _action { const gchar * name; /**< Name of the action */ GCallback callback; /**< Callback for "activated/toggled" signal */ } action[] = { // Offline actions { "Connect", G_CALLBACK(action_connect) }, { "LoadScreenDump", G_CALLBACK(action_loadscreendump) }, { "SetHostname", G_CALLBACK(action_sethostname) }, { "TestPattern", G_CALLBACK(lib3270_testpattern) }, // Online actions { "Redraw", G_CALLBACK(action_redraw) }, { "SaveScreen", G_CALLBACK(action_savescreen) }, { "PrintScreen", G_CALLBACK(action_printscreen) }, { "DumpScreen", G_CALLBACK(action_dumpscreen) }, { "Disconnect", G_CALLBACK(action_disconnect) }, // Select actions { "SelectField", G_CALLBACK(action_selectfield) }, { "SelectRight", G_CALLBACK(action_selectright) }, { "SelectLeft", G_CALLBACK(action_selectleft) }, { "SelectUp", G_CALLBACK(action_selectup) }, { "SelectDown", G_CALLBACK(action_selectdown) }, { "SelectionRight", G_CALLBACK(action_selectionright) }, { "SelectionLeft", G_CALLBACK(action_selectionleft) }, { "SelectionUp", G_CALLBACK(action_selectionup) }, { "SelectionDown", G_CALLBACK(action_selectiondown) }, // Cursor Movement { "CursorRight", G_CALLBACK(action_Right) }, { "CursorLeft", G_CALLBACK(action_Left) }, { "CursorUp", G_CALLBACK(action_Up) }, { "CursorDown", G_CALLBACK(action_Down) }, { "Newline", G_CALLBACK(action_Newline) }, { "NextField", G_CALLBACK(lib3270_tab) }, { "PreviousField", G_CALLBACK(lib3270_backtab) }, { "kpadd", G_CALLBACK(action_kpadd) }, { "kpsubtract", G_CALLBACK(action_kpsubtract) }, // Edit actions { "PasteNext", G_CALLBACK(action_pastenext) }, { "PasteTextFile", G_CALLBACK(action_pastetextfile) }, { "Reselect", G_CALLBACK(action_reselect) }, { "SelectAll", G_CALLBACK(action_selectall) }, { "EraseInput", G_CALLBACK(erase_input_action) }, { "Clear", G_CALLBACK(clear_action) }, { "Reset", G_CALLBACK(action_Reset) }, { "Escape", G_CALLBACK(action_Reset) }, // File-transfer actions { "Download", G_CALLBACK(action_download) }, { "Upload", G_CALLBACK(action_upload) }, // Selection actions { "Append", G_CALLBACK(action_append) }, { "Unselect", G_CALLBACK(action_unselect) }, { "Copy", G_CALLBACK(action_copy) }, { "CopyAsTable", G_CALLBACK(action_copyastable) }, { "CopyAsImage", G_CALLBACK(action_copyasimage) }, { "PrintSelected", G_CALLBACK(action_printselected) }, { "SaveSelected", G_CALLBACK(action_saveselected) }, { "PrintClipboard", G_CALLBACK(action_printclipboard) }, { "SaveClipboard", G_CALLBACK(action_saveclipboard) }, { "Paste", G_CALLBACK(action_paste) }, { "FileMenu", NULL }, { "FTMenu", NULL }, { "NetworkMenu", NULL }, { "HelpMenu", NULL }, { "EditMenu", NULL }, { "OptionsMenu", NULL }, { "SettingsMenu", NULL }, { "ScriptsMenu", NULL }, { "ViewMenu", NULL }, { "InputMethod", NULL }, { "ToolbarMenu", NULL }, { "DebugMenu", NULL }, { "FontSettings", NULL }, { "Preferences", NULL }, { "Network", NULL }, { "Properties", NULL }, { "ScreenSizes", NULL }, { "About", G_CALLBACK(action_about) }, { "Quit", G_CALLBACK(action_quit) }, { "SelectColors", G_CALLBACK(action_selectcolors) }, { "Save", NULL }, { "Return", G_CALLBACK(action_enter) }, { "Enter", G_CALLBACK(action_enter) }, // Lib3270 calls { "EraseEOF", G_CALLBACK(lib3270_eraseeof) }, { "EraseEOL", G_CALLBACK(lib3270_eraseeol) }, { "Home", G_CALLBACK(lib3270_firstfield) }, { "DeleteWord", G_CALLBACK(lib3270_deleteword) }, { "EraseField", G_CALLBACK(lib3270_deletefield) }, { "Delete", G_CALLBACK(lib3270_delete) }, { "Erase", G_CALLBACK(lib3270_erase) }, { "SysREQ", G_CALLBACK(lib3270_sysreq) }, { "Attn", G_CALLBACK(lib3270_attn) }, { "Break", G_CALLBACK(lib3270_break) }, { "Dup", G_CALLBACK(lib3270_dup) }, { "FirstField", G_CALLBACK(lib3270_firstfield) }, }; int f; if(name) *name = NULL; memset(info,0,sizeof(UI_CALLBACK)); if(!g_ascii_strcasecmp(key,"pfkey")) { info->type = UI_CALLBACK_TYPE_DEFAULT; info->label = "pfkey"; info->callback = G_CALLBACK(action_pfkey); info->user_data = (gpointer) atoi(get_xml_attribute(names, values, "id")); if(name) *name = g_strdup_printf("pf%02d",(int) info->user_data); return 0; } if(!g_ascii_strcasecmp(key,"pakey")) { info->type = UI_CALLBACK_TYPE_DEFAULT; info->label = "pakey"; info->callback = G_CALLBACK(action_pakey); info->user_data = (gpointer) atoi(get_xml_attribute(names, values, "id")); if(name) *name = g_strdup_printf("pa%02d",(int) info->user_data); return 0; } if(!g_ascii_strcasecmp(key,"toggle")) { // Check for lib3270 toggles const gchar *id = get_xml_attribute(names, values, "id"); for(f=0;f<N_TOGGLES;f++) { if(!g_ascii_strcasecmp(id,get_toggle_name(f))) { info->type = UI_CALLBACK_TYPE_TOGGLE; info->callback = G_CALLBACK(toggle_action); info->user_data = (gpointer) f; if(name) *name = g_strconcat("Toggle",get_toggle_name(f),NULL); return 0; } } // Check for GUI toggles for(f=0;f<G_N_ELEMENTS(gui_toggle_info);f++) { if(!g_ascii_strcasecmp(id,gui_toggle_info[f].name)) { info->type = UI_CALLBACK_TYPE_TOGGLE; info->callback = G_CALLBACK(toggle_gui); info->user_data = (gpointer) f; if(name) *name = g_strconcat("Toggle",gui_toggle_info[f].name,NULL); return 0; } } if(!g_ascii_strcasecmp(id,"gdkdebug")) { info->type = UI_CALLBACK_TYPE_TOGGLE; info->callback = G_CALLBACK(action_ToggleGDKDebug); info->user_data = (gpointer) f; if(name) *name = g_strdup("ToggleGDKDebug"); return 0; } return EINVAL; } if(!g_ascii_strcasecmp(key,"toggleset")) { const gchar *id = get_xml_attribute(names, values, "id"); for(f=0;f<N_TOGGLES;f++) { if(!g_ascii_strcasecmp(id,get_toggle_name(f))) { info->type = UI_CALLBACK_TYPE_DEFAULT; // info->label = toggle_info[f].set_label ? toggle_info[f].set_label : toggle_info[f].do_label; info->callback = G_CALLBACK(toggle_set_action); info->user_data = (gpointer) f; if(name) *name = g_strconcat("ToggleSet",get_toggle_name(f),NULL); return 0; } } return EINVAL; } if(!g_ascii_strcasecmp(key,"togglereset")) { const gchar *id = get_xml_attribute(names, values, "id"); for(f=0;f<N_TOGGLES;f++) { if(!g_ascii_strcasecmp(id,get_toggle_name(f))) { info->type = UI_CALLBACK_TYPE_DEFAULT; // info->label = toggle_info[f].reset_label ? toggle_info[f].reset_label : toggle_info[f].do_label; info->callback = G_CALLBACK(toggle_reset_action); info->user_data = (gpointer) f; if(name) *name = g_strconcat("ToggleReset",get_toggle_name(f),NULL); return 0; } } return EINVAL; } // Search internal actions for(f=0;f<G_N_ELEMENTS(action);f++) { if(!g_ascii_strcasecmp(key,action[f].name)) { info->type = UI_CALLBACK_TYPE_DEFAULT; // info->label = action[f].label; info->callback = action[f].callback; return 0; } } // Search for plugin actions if(strchr(key,'.')) { gchar *plugin_name = g_strdup(key); gchar *entry_name = strchr(plugin_name,'.'); GModule *plugin; *(entry_name++) = 0; plugin = get_plugin_by_name(plugin_name); if(plugin) { gpointer cbk; if(get_symbol_by_name(plugin,&cbk,"action_%s_activated",entry_name)) { info->type = UI_CALLBACK_TYPE_DEFAULT; info->callback = (GCallback) cbk; info->user_data = (gpointer) topwindow; } else if(get_symbol_by_name(plugin,&cbk,"action_%s_toggled",entry_name)) { info->type = UI_CALLBACK_TYPE_TOGGLE; info->callback = (GCallback) cbk; info->user_data = (gpointer) topwindow; } } g_free(plugin_name); if(info->callback) return 0; } // Check if it's an external program action if(!g_ascii_strncasecmp(key,"call",4) && strlen(key) > 5 && g_ascii_isspace(*(key+4))) { } return ENOENT; }
/* * Decodes map data from a <data> node */ static void decode_layer_data(xmlNode *data_node, ALLEGRO_MAP_LAYER *layer) { char *str = g_strstrip((char *)data_node->children->content); int datalen = layer->width * layer->height; layer->data = (char *)calloc(datalen, sizeof(char)); char *encoding = get_xml_attribute(data_node, "encoding"); if (!encoding) { int i = 0; GSList *tiles = get_children_for_name(data_node, "tile"); GSList *tile_item = tiles; while (tile_item) { xmlNode *tile_node = (xmlNode*)tile_item->data; tile_item = g_slist_next(tile_item); char *gid = get_xml_attribute(tile_node, "gid"); layer->data[i] = atoi(gid); i++; } g_slist_free(tiles); } else if (!strcmp(encoding, "base64")) { // decompress gsize rawlen; unsigned char *rawdata = g_base64_decode(str, &rawlen); // check the compression char *compression = get_xml_attribute(data_node, "compression"); if (compression != NULL) { if (strcmp(compression, "zlib") && strcmp(compression, "gzip")) { fprintf(stderr, "Error: unknown compression format '%s'\n", compression); return; } // set up files used by zlib to decompress the data ALLEGRO_PATH *srcpath; ALLEGRO_FILE *datasrc = al_make_temp_file("XXXXXX", &srcpath); al_fwrite(datasrc, rawdata, rawlen); al_fseek(datasrc, 0, ALLEGRO_SEEK_SET); //al_fclose(datasrc); //datasrc = al_fopen(al_path_cstr(srcpath, ALLEGRO_NATIVE_PATH_SEP), "rb"); ALLEGRO_FILE *datadest = al_make_temp_file("XXXXXX", NULL); // decompress and print an error if it failed int status = inf(datasrc, datadest); if (status) zerr(status); // flush data and get the file length al_fflush(datadest); int len = al_fsize(datadest); // read in the file al_fseek(datadest, 0, ALLEGRO_SEEK_SET); char *data = (char *)calloc(len, sizeof(char)); if (al_fread(datadest, data, len) != len) { fprintf(stderr, "Error: failed to read in map data\n"); return; } // every tile id takes 4 bytes int i; for (i = 0; i<len; i += 4) { int tileid = 0; tileid |= data[i]; tileid |= data[i+1] << 8; tileid |= data[i+2] << 16; tileid |= data[i+3] << 24; layer->data[i/4] = tileid; } /* printf("layer dimensions: %dx%d, data length = %d\n", layer->width, layer->height, len); */ al_destroy_path(srcpath); al_fclose(datasrc); al_fclose(datadest); al_free(data); } else { // TODO: verify that this still works int i; for (i = 0; i<rawlen; i += 4) { int tileid = 0; tileid |= rawdata[i]; tileid |= rawdata[i+1] << 8; tileid |= rawdata[i+2] << 16; tileid |= rawdata[i+3] << 24; layer->data[i/4] = tileid; } } g_free(rawdata); } else if (!strcmp(encoding, "csv")) { int i; for (i = 0; i<datalen; i++) { char *id = strtok((i == 0 ? str : NULL), ","); layer->data[i] = atoi(id); } } else { fprintf(stderr, "Error: unknown encoding format '%s'\n", encoding); } }
/* * Parse a map file into an `ALLEGRO_MAP` struct. By default, relative values of `dir` * will be resolved relative to the path of the running executable. To change this, * call `al_find_resources_as(RELATIVE_TO_CWD);` first. */ ALLEGRO_MAP *al_open_map(const char *dir, const char *filename) { char *cwd = al_get_current_directory(); if (!cwd) { fprintf(stderr, "failed to get cwd; errno = %d", al_get_errno()); return NULL; } // `resources` will point to either cwd or the location of the running executable, // depending on what was passed in for `rel`. ALLEGRO_PATH *resources; switch (resources_rel_to) { case RELATIVE_TO_EXE: resources = al_get_standard_path(ALLEGRO_RESOURCES_PATH); break; case RELATIVE_TO_CWD: resources = al_create_path_for_directory(cwd); break; default: fprintf(stderr, "unexpected value for `resources_rel_to` in al_open_map(): %d\n", resources_rel_to); al_free(cwd); return NULL; } ALLEGRO_PATH *map_dir = al_create_path_for_directory(dir); // Change directory to <cwd>/dir if dir is relative, otherwise dir. ALLEGRO_PATH *new_path = (al_join_paths(resources, map_dir) ? resources : map_dir); const char *new_path_cstr = al_path_cstr(new_path, ALLEGRO_NATIVE_PATH_SEP); if (!al_change_directory(new_path_cstr)) { fprintf(stderr, "Error: failed to cd into `%s` in al_open_map().\n", new_path_cstr); al_destroy_path(resources); al_destroy_path(map_dir); al_free(cwd); return NULL; } al_destroy_path(resources); al_destroy_path(map_dir); // Read in the data file xmlDoc *doc = xmlReadFile(filename, NULL, 0); if (!doc) { fprintf(stderr, "Error: failed to parse map data: %s\n", filename); al_free(cwd); return NULL; } // Get the root element, <map> xmlNode *root = xmlDocGetRootElement(doc); // Get some basic info ALLEGRO_MAP *map = MALLOC(ALLEGRO_MAP); map->width = atoi(get_xml_attribute(root, "width")); map->height = atoi(get_xml_attribute(root, "height")); map->tile_width = atoi(get_xml_attribute(root, "tilewidth")); map->tile_height = atoi(get_xml_attribute(root, "tileheight")); map->orientation = g_strdup(get_xml_attribute(root, "orientation")); map->tile_layer_count = 0; map->object_layer_count = 0; // Get the tilesets GSList *tilesets = get_children_for_name(root, "tileset"); map->tilesets = NULL; GSList *tileset_item = tilesets; while (tileset_item) { xmlNode *tileset_node = (xmlNode*)tileset_item->data; tileset_item = g_slist_next(tileset_item); ALLEGRO_MAP_TILESET *tileset = MALLOC(ALLEGRO_MAP_TILESET); tileset->firstgid = atoi(get_xml_attribute(tileset_node, "firstgid")); tileset->tilewidth = atoi(get_xml_attribute(tileset_node, "tilewidth")); tileset->tileheight = atoi(get_xml_attribute(tileset_node, "tileheight")); tileset->name = g_strdup(get_xml_attribute(tileset_node, "name")); // Get this tileset's image xmlNode *image_node = get_first_child_for_name(tileset_node, "image"); tileset->width = atoi(get_xml_attribute(image_node, "width")); tileset->height = atoi(get_xml_attribute(image_node, "height")); tileset->source = g_strdup(get_xml_attribute(image_node, "source")); tileset->bitmap = al_load_bitmap(tileset->source); if (!tileset->bitmap) { fprintf(stderr, "Error: no bitmap available; did you load the image addon?\n"); } // Get this tileset's tiles GSList *tiles = get_children_for_name(tileset_node, "tile"); tileset->tiles = NULL; GSList *tile_item = tiles; while (tile_item) { xmlNode *tile_node = (xmlNode*)tile_item->data; tile_item = g_slist_next(tile_item); ALLEGRO_MAP_TILE *tile = MALLOC(ALLEGRO_MAP_TILE); tile->id = tileset->firstgid + atoi(get_xml_attribute(tile_node, "id")); tile->tileset = tileset; tile->bitmap = NULL; // Get this tile's properties tile->properties = parse_properties(tile_node); // TODO: add a destructor tileset->tiles = g_slist_prepend(tileset->tiles, tile); } g_slist_free(tiles); //tileset->tiles = g_slist_reverse(tileset->tiles); // TODO: add a destructor map->tilesets = g_slist_prepend(map->tilesets, tileset); } g_slist_free(tilesets); //map->tilesets = g_slist_reverse(map->tilesets); // Create the map's master list of tiles cache_tile_list(map); // Get the layers GSList *layers = get_children_for_either_name(root, "layer", "objectgroup"); map->layers = NULL; GSList *layer_item = layers; while (layer_item) { xmlNode *layer_node = (xmlNode*)layer_item->data; layer_item = g_slist_next(layer_item); ALLEGRO_MAP_LAYER *layer = MALLOC(ALLEGRO_MAP_LAYER); layer->name = g_strdup(get_xml_attribute(layer_node, "name")); layer->properties = parse_properties(layer_node); char *layer_visible = get_xml_attribute(layer_node, "visible"); layer->visible = (layer_visible != NULL ? atoi(layer_visible) : 1); char *layer_opacity = get_xml_attribute(layer_node, "opacity"); layer->opacity = (layer_opacity != NULL ? atof(layer_opacity) : 1.0); if (!strcmp((const char*)layer_node->name, "layer")) { layer->type = TILE_LAYER; layer->width = atoi(get_xml_attribute(layer_node, "width")); layer->height = atoi(get_xml_attribute(layer_node, "height")); decode_layer_data(get_first_child_for_name(layer_node, "data"), layer); // Create any missing tile objects unsigned i, j; for (i = 0; i < layer->height; i++) { for (j = 0; j < layer->width; j++) { char id = al_get_single_tile_id(layer, j, i); if (id == 0) { continue; } ALLEGRO_MAP_TILE *tile = al_get_tile_for_id(map, id); if (!tile) { // wasn't defined in the map file, presumably because it had no properties tile = MALLOC(ALLEGRO_MAP_TILE); tile->id = id; tile->properties = g_hash_table_new(NULL, NULL); tile->tileset = NULL; tile->bitmap = NULL; // locate its tilemap GSList *tilesets = map->tilesets; ALLEGRO_MAP_TILESET *tileset_ref; while (tilesets) { ALLEGRO_MAP_TILESET *tileset = (ALLEGRO_MAP_TILESET*)tilesets->data; tilesets = g_slist_next(tilesets); if (tileset->firstgid <= id) { if (!tile->tileset || tileset->firstgid > tile->tileset->firstgid) { tileset_ref = tileset; } } } tile->tileset = tileset_ref; tileset_ref->tiles = g_slist_prepend(tileset_ref->tiles, tile); g_hash_table_insert(map->tiles, GINT_TO_POINTER(tile->id), tile); } // create this tile's bitmap if it hasn't been yet if (!tile->bitmap) { ALLEGRO_MAP_TILESET *tileset = tile->tileset; int id = tile->id - tileset->firstgid; int width = tileset->width / tileset->tilewidth; int x = (id % width) * tileset->tilewidth; int y = (id / width) * tileset->tileheight; if (tileset->bitmap) { tile->bitmap = al_create_sub_bitmap( tileset->bitmap, x, y, tileset->tilewidth, tileset->tileheight); } } } } map->tile_layer_count++; map->tile_layers = g_slist_prepend(map->tile_layers, layer); } else if (!strcmp((const char*)layer_node->name, "objectgroup")) { layer->type = OBJECT_LAYER; layer->objects = NULL; layer->object_count = 0; // TODO: color? GSList *objects = get_children_for_name(layer_node, "object"); GSList *object_item = objects; while (object_item) { xmlNode *object_node = (xmlNode*)object_item->data; object_item = g_slist_next(object_item); ALLEGRO_MAP_OBJECT *object = MALLOC(ALLEGRO_MAP_OBJECT); object->layer = layer; object->name = g_strdup(get_xml_attribute(object_node, "name")); object->type = g_strdup(get_xml_attribute(object_node, "type")); object->x = atoi(get_xml_attribute(object_node, "x")); object->y = atoi(get_xml_attribute(object_node, "y")); object->bitmap = NULL; char *object_width = get_xml_attribute(object_node, "width"); object->width = (object_width ? atoi(object_width) : 0); char *object_height = get_xml_attribute(object_node, "height"); object->height = (object_height ? atoi(object_height) : 0); char *gid = get_xml_attribute(object_node, "gid"); object->gid = (gid ? atoi(gid) : 0); char *object_visible = get_xml_attribute(object_node, "visible"); object->visible = (object_visible ? atoi(object_visible) : 1); // Get the object's properties object->properties = parse_properties(object_node); layer->objects = g_slist_prepend(layer->objects, object); layer->object_count++; } map->object_layer_count++; map->object_layers = g_slist_prepend(map->object_layers, layer); } else { fprintf(stderr, "Error: found invalid layer node \"%s\"\n", layer_node->name); continue; } map->layers = g_slist_prepend(map->layers, layer); } g_slist_free(layers); // If any objects have a tile gid, cache their image layer_item = map->layers; while (layer_item) { ALLEGRO_MAP_LAYER *layer = (ALLEGRO_MAP_LAYER*)layer_item->data; layer_item = g_slist_next(layer_item); if (layer->type != OBJECT_LAYER) { continue; } GSList *objects = layer->objects; while (objects) { ALLEGRO_MAP_OBJECT *object = (ALLEGRO_MAP_OBJECT*)objects->data; objects = g_slist_next(objects); if (!object->gid) { continue; } object->bitmap = al_get_tile_for_id(map, object->gid)->bitmap; object->width = map->tile_width; object->height = map->tile_height; } } xmlFreeDoc(doc); al_change_directory(cwd); al_free(cwd); return map; }
TILED_MAP* tiled_load_tmx_file (const char *filename) { TILED_MAP *map; xmlDoc *doc; xmlNode *root; char *str; if (!filename) return NULL; LIBXML_TEST_VERSION doc = xmlReadFile (filename, NULL, 0); if (!doc) return NULL; ALLEGRO_PATH *mapdir = al_create_path (filename); al_set_path_filename (mapdir, NULL); if (!al_change_directory (al_path_cstr (mapdir, ALLEGRO_NATIVE_PATH_SEP))) { printf ("Failed to change directory."); } al_destroy_path (mapdir); root = xmlDocGetRootElement (doc); map = al_malloc (sizeof (TILED_MAP)); map->width = get_int (root, "width", 0); map->height = get_int (root, "height", 0); map->tile_width = get_int (root, "tilewidth", 0); map->tile_height = get_int (root, "tileheight", 0); map->strings = _al_list_create (); map->properties = get_properties (root, map); map->tiles = NULL; str = get_xml_attribute (root, "orientation"); if (!strcmp (str, "orthogonal")) map->orientation = ORIENTATION_ORTHOGONAL; else if (!strcmp (str, "isometric")) map->orientation = ORIENTATION_ISOMETRIC; else if (!strcmp (str, "staggered")) map->orientation = ORIENTATION_STAGGERED; else map->orientation = ORIENTATION_UNKNOWN; // Tilesets LIST *tileset_nodes = get_children_for_name (root, 1, "tileset"); map->tilesets = create_list (_al_list_size (tileset_nodes)); LIST_ITEM *tileset_item = _al_list_front (tileset_nodes); while (tileset_item) { xmlNode *tileset_node = _al_list_item_data (tileset_item); TILED_TILESET *tileset = al_malloc (sizeof (TILED_TILESET)); tileset->first_gid = get_int (tileset_node, "firstgid", 1); tileset->tile_width = get_int (tileset_node, "tilewidth", 0); tileset->tile_height = get_int (tileset_node, "tileheight", 0); tileset->name = get_str (tileset_node, "name"); tileset->properties = get_properties (tileset_node, map); xmlNode *image_node = get_first_child_for_name (tileset_node, "image"); tileset->image_width = get_int (image_node, "width", 0); tileset->image_height = get_int (image_node, "height", 0); tileset->image_source = get_str (image_node, "source"); tileset->bitmap = al_load_bitmap (tileset->image_source); LIST *tile_nodes = get_children_for_name (tileset_node, 1, "tile"); int tiles_per_row = tileset->image_width / tileset->tile_width; tileset->num_tiles = (tileset->image_width * tileset->image_height) / (tileset->tile_width * tileset->tile_height); TILED_TILE *tile; tileset->tiles = al_malloc (tileset->num_tiles * sizeof (TILED_TILE)); for (int i = 0; i < tileset->num_tiles; i++) { tile = &tileset->tiles[i]; tile->id = i; tile->gid = i + tileset->first_gid; tile->tileset = tileset; tile->properties = NULL; int x = (i % tiles_per_row) * tileset->tile_width; int y = (i / tiles_per_row) * tileset->tile_height; tile->bitmap = al_create_sub_bitmap(tileset->bitmap, x, y, tileset->tile_width, tileset->tile_height); map->tiles = aa_insert (map->tiles, &tile->gid, tile, intcmp); } LIST_ITEM *tile_item = _al_list_front (tile_nodes); while (tile_item) { xmlNode *tile_node = (xmlNode*)_al_list_item_data (tile_item); int id = get_int (tile_node, "id", 0); tile = &tileset->tiles[id]; tile->properties = get_properties (tile_node, map); tile_item = _al_list_next (tile_nodes, tile_item); } _al_list_destroy (tile_nodes); _al_list_push_back_ex (map->tilesets, tileset, dtor_tileset); tileset_item = _al_list_next (tileset_nodes, tileset_item); } _al_list_destroy (tileset_nodes); // Layers LIST *layer_nodes = get_children_for_name (root, 2, "layer", "objectgroup"); map->layers = create_list (_al_list_size (layer_nodes)); map->layers_fore = _al_list_create (); map->layers_back = _al_list_create (); LIST_ITEM *layer_item = _al_list_front (layer_nodes); while (layer_item) { xmlNode *layer_node = _al_list_item_data (layer_item); TILED_LAYER *layer = NULL; const char* type_str = (const char *)layer_node->name; if (!strcmp (type_str, "layer")) { layer = al_malloc (sizeof (TILED_LAYER_TILE)); layer->type = LAYER_TYPE_TILE; } else if (!strcmp (type_str, "objectgroup")) { layer = al_malloc (sizeof (TILED_LAYER_OBJECT)); layer->type = LAYER_TYPE_OBJECT; } layer->name = get_str (layer_node, "name"); layer->width = get_int (layer_node, "width", 0); layer->height = get_int (layer_node, "height", 0); layer->opacity = get_float (layer_node, "opacity", 1.0); layer->map = map; layer->properties = get_properties (layer_node, map); char *order = aa_search (layer->properties, "order", charcmp); if (layer->type == LAYER_TYPE_TILE) { TILED_LAYER_TILE *tile_layer = (TILED_LAYER_TILE *)layer; tile_layer->tiles = al_malloc (layer->height * sizeof (TILED_TILE*)); xmlNode *data_node = get_first_child_for_name (layer_node, "data"); LIST *tile_nodes = get_children_for_name (data_node, 1, "tile"); LIST_ITEM *tile_item = _al_list_front (tile_nodes); for (int j = 0; j < layer->height; j++) { tile_layer->tiles[j] = al_malloc (layer->width * sizeof (TILED_TILE*)); for (int k = 0; k < layer->width; k++) { xmlNode *tile_node = (xmlNode*)_al_list_item_data (tile_item); int id = get_int (tile_node, "gid", 0); if (id == 0) { tile_layer->tiles[j][k] = NULL; } else { TILED_TILE *tile = aa_search (map->tiles, &id, intcmp); tile_layer->tiles[j][k] = tile; } tile_item = _al_list_next (tile_nodes, tile_item); } } _al_list_destroy (tile_nodes); } else if (layer->type == LAYER_TYPE_OBJECT) { TILED_LAYER_OBJECT *object_layer = (TILED_LAYER_OBJECT *)layer; object_layer->objects = _al_list_create (); LIST *object_nodes = get_children_for_name (layer_node, 1, "object"); LIST_ITEM *object_item = _al_list_front (object_nodes); while (object_item) { xmlNode *object_node = (xmlNode*)_al_list_item_data (object_item); TILED_OBJECT *cobj = NULL; int width = get_int (object_node, "width", 0); int gid = get_int (object_node, "gid", 0); int px = get_int (object_node, "x", 0); int py = get_int (object_node, "y", 0); if (width > 0) { TILED_OBJECT_RECT *obj = al_malloc (sizeof (TILED_OBJECT_RECT)); obj->object.type = OBJECT_TYPE_RECT; obj->width = width; obj->height = get_int (object_node, "height", 0); cobj = (TILED_OBJECT *) obj; } else if (gid > 0) { TILED_OBJECT_TILE *obj = al_malloc (sizeof (TILED_OBJECT_TILE)); obj->object.type = OBJECT_TYPE_TILE; obj->tile = aa_search (map->tiles, &gid, intcmp); cobj = (TILED_OBJECT *) obj; } else { TILED_OBJECT_GEOM *obj = al_malloc (sizeof (TILED_OBJECT_GEOM)); obj->object.type = OBJECT_TYPE_GEOM; xmlNode *poly_node = get_first_child_for_name (object_node, "polyline"); if (poly_node) { obj->points = get_float_points (poly_node, "points", &obj->num_points); offset_points (px, py, obj->points, obj->num_points); } else { poly_node = get_first_child_for_name (object_node, "polygon"); if (poly_node) obj->points = get_float_points (poly_node, "points", &obj->num_points); offset_points (px, py, obj->points, obj->num_points); } cobj = (TILED_OBJECT *) obj; } cobj->x = px; cobj->y = py; cobj->name = get_str (object_node, "name"); cobj->type_str = get_str (object_node, "type"); cobj->properties = get_properties (object_node, map); _al_list_push_back_ex (object_layer->objects, cobj, dtor_object); object_item = _al_list_next (object_nodes, object_item); } _al_list_destroy (object_nodes); } layer_item = _al_list_next (layer_nodes, layer_item); _al_list_push_back_ex (map->layers, layer, dtor_layer); if (order && !strcmp (order, "fore")) _al_list_push_back (map->layers_fore, layer); else _al_list_push_back (map->layers_back, layer); } _al_list_destroy (layer_nodes); xmlFreeDoc (doc); xmlCleanupParser (); ALLEGRO_PATH *respath = al_get_standard_path (ALLEGRO_RESOURCES_PATH); al_change_directory (al_path_cstr (respath, ALLEGRO_NATIVE_PATH_SEP)); al_destroy_path (respath); return map; }
static inline char *get_str (xmlNode *node, const char *name) { char *str = get_xml_attribute (node, name); return str ? strdup (str) : NULL; }
static inline float get_float (xmlNode *node, const char *name, float def) { char *attr = get_xml_attribute (node, name); return attr ? atof (attr) : def; }
static inline int get_int (xmlNode *node, const char *name, int def) { char *attr = get_xml_attribute (node, name); return attr ? atoi (attr) : def; }