tileset_t* read_tileset(FILE* file) { image_t* atlas = NULL; int atlas_w, atlas_h; long file_pos; int n_tiles_per_row; struct rts_header rts; rect_t segment; struct rts_tile_header tilehdr; struct tile* tiles = NULL; tileset_t* tileset = NULL; int i, j; memset(&rts, 0, sizeof(struct rts_header)); if (file == NULL) goto on_error; file_pos = ftell(file); if ((tileset = calloc(1, sizeof(tileset_t))) == NULL) goto on_error; if (fread(&rts, sizeof(struct rts_header), 1, file) != 1) goto on_error; if (memcmp(rts.signature, ".rts", 4) != 0 || rts.version < 1 || rts.version > 1) goto on_error; if (rts.tile_bpp != 32) goto on_error; if (!(tiles = calloc(rts.num_tiles, sizeof(struct tile)))) goto on_error; // prepare the tile atlas n_tiles_per_row = ceil(sqrt(rts.num_tiles)); atlas_w = rts.tile_width * n_tiles_per_row; atlas_h = rts.tile_height * n_tiles_per_row; if (!(atlas = create_image(atlas_w, atlas_h))) goto on_error; // read in tile bitmaps for (i = 0; i < rts.num_tiles; ++i) { tiles[i].image = read_subimage(file, atlas, i % n_tiles_per_row * rts.tile_width, i / n_tiles_per_row * rts.tile_height, rts.tile_width, rts.tile_height); if (tiles[i].image == NULL) goto on_error; } // read in tile headers and obstruction maps for (i = 0; i < rts.num_tiles; ++i) { if (fread(&tilehdr, sizeof(struct rts_tile_header), 1, file) != 1) goto on_error; tiles[i].name = read_lstring_raw(file, tilehdr.name_length, true); tiles[i].next_index = tilehdr.animated ? tilehdr.next_tile : i; tiles[i].delay = tilehdr.animated ? tilehdr.delay : 0; tiles[i].animate_index = i; tiles[i].frames_left = tiles[i].delay; if (rts.has_obstructions) { switch (tilehdr.obsmap_type) { case 1: // pixel-perfect obstruction (no longer supported) fseek(file, rts.tile_width * rts.tile_height, SEEK_CUR); break; case 2: // line segment-based obstruction tiles[i].num_obs_lines = tilehdr.num_segments; if ((tiles[i].obsmap = new_obsmap()) == NULL) goto on_error; for (j = 0; j < tilehdr.num_segments; ++j) { if (!fread_rect_16(file, &segment)) goto on_error; add_obsmap_line(tiles[i].obsmap, segment); } break; default: goto on_error; } } } // wrap things up free_image(atlas); tileset->width = rts.tile_width; tileset->height = rts.tile_height; tileset->num_tiles = rts.num_tiles; tileset->tiles = tiles; return tileset; on_error: // oh no! if (file != NULL) fseek(file, file_pos, SEEK_SET); if (tiles != NULL) { for (i = 0; i < rts.num_tiles; ++i) { free_lstring(tiles[i].name); free_obsmap(tiles[i].obsmap); free_image(tiles[i].image); } free(tileset->tiles); } free_image(atlas); free(tileset); return NULL; }
font_t* load_font(const char* filename) { image_t* atlas = NULL; int atlas_x, atlas_y; int atlas_size_x, atlas_size_y; sfs_file_t* file; font_t* font = NULL; struct font_glyph* glyph; struct rfn_glyph_header glyph_hdr; long glyph_start; uint8_t* grayscale; image_lock_t* lock = NULL; int max_x = 0, max_y = 0; int min_width = INT_MAX; int64_t n_glyphs_per_row; int pixel_size; struct rfn_header rfn; uint8_t *psrc; color_t *pdest; int i, x, y; console_log(2, "loading font #%u as `%s`", s_next_font_id, filename); memset(&rfn, 0, sizeof(struct rfn_header)); if ((file = sfs_fopen(g_fs, filename, NULL, "rb")) == NULL) goto on_error; if (!(font = calloc(1, sizeof(font_t)))) goto on_error; if (sfs_fread(&rfn, sizeof(struct rfn_header), 1, file) != 1) goto on_error; pixel_size = (rfn.version == 1) ? 1 : 4; if (!(font->glyphs = calloc(rfn.num_chars, sizeof(struct font_glyph)))) goto on_error; // pass 1: load glyph headers and find largest glyph glyph_start = sfs_ftell(file); for (i = 0; i < rfn.num_chars; ++i) { glyph = &font->glyphs[i]; if (sfs_fread(&glyph_hdr, sizeof(struct rfn_glyph_header), 1, file) != 1) goto on_error; sfs_fseek(file, glyph_hdr.width * glyph_hdr.height * pixel_size, SFS_SEEK_CUR); max_x = fmax(glyph_hdr.width, max_x); max_y = fmax(glyph_hdr.height, max_y); min_width = fmin(min_width, glyph_hdr.width); glyph->width = glyph_hdr.width; glyph->height = glyph_hdr.height; } font->num_glyphs = rfn.num_chars; font->min_width = min_width; font->max_width = max_x; font->height = max_y; // create glyph atlas n_glyphs_per_row = ceil(sqrt(rfn.num_chars)); atlas_size_x = max_x * n_glyphs_per_row; atlas_size_y = max_y * n_glyphs_per_row; if ((atlas = create_image(atlas_size_x, atlas_size_y)) == NULL) goto on_error; // pass 2: load glyph data sfs_fseek(file, glyph_start, SFS_SEEK_SET); if (!(lock = lock_image(atlas))) goto on_error; for (i = 0; i < rfn.num_chars; ++i) { glyph = &font->glyphs[i]; if (sfs_fread(&glyph_hdr, sizeof(struct rfn_glyph_header), 1, file) != 1) goto on_error; atlas_x = i % n_glyphs_per_row * max_x; atlas_y = i / n_glyphs_per_row * max_y; switch (rfn.version) { case 1: // RFN v1: 8-bit grayscale glyphs if (!(glyph->image = create_subimage(atlas, atlas_x, atlas_y, glyph_hdr.width, glyph_hdr.height))) goto on_error; grayscale = malloc(glyph_hdr.width * glyph_hdr.height); if (sfs_fread(grayscale, glyph_hdr.width * glyph_hdr.height, 1, file) != 1) goto on_error; psrc = grayscale; pdest = lock->pixels + atlas_x + atlas_y * lock->pitch; for (y = 0; y < glyph_hdr.height; ++y) { for (x = 0; x < glyph_hdr.width; ++x) pdest[x] = color_new(psrc[x], psrc[x], psrc[x], 255); pdest += lock->pitch; psrc += glyph_hdr.width; } break; case 2: // RFN v2: 32-bit truecolor glyphs if (!(glyph->image = read_subimage(file, atlas, atlas_x, atlas_y, glyph_hdr.width, glyph_hdr.height))) goto on_error; break; } } unlock_image(atlas, lock); sfs_fclose(file); free_image(atlas); font->id = s_next_font_id++; return ref_font(font); on_error: console_log(2, "failed to load font #%u", s_next_font_id++); sfs_fclose(file); if (font != NULL) { for (i = 0; i < rfn.num_chars; ++i) { if (font->glyphs[i].image != NULL) free_image(font->glyphs[i].image); } free(font->glyphs); free(font); } if (lock != NULL) unlock_image(atlas, lock); if (atlas != NULL) free_image(atlas); return NULL; }