Example #1
0
SDL_Surface* render_text(sd_font *font, const char *text, int area_w) {
    // Vars
    unsigned int rmask,gmask,bmask,amask;
    int char_w, char_h;
    int pix_w, pix_h;
    int slen;
    SDL_Surface *surface;
    SDL_Surface *tmp;
    SDL_Rect dst;
    sd_rgba_image *img = 0;
    
    // Required surface size
    slen = strlen(text);
    char_h = (slen * 8) / area_w + 1;
    pix_h = char_h * font->h;
    if(char_h == 1) {
        char_w = slen;
        pix_w = slen * 8;
    } else {
        char_w = area_w / 8;
        pix_w = area_w;
    }

    // Create an empty SDL surface
    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
    amask = 0xff000000;
    if((surface = SDL_CreateRGBSurface(0, pix_w, pix_h, 32, rmask, gmask, bmask, amask)) == 0) {
        return 0;
    }
    if((tmp = SDL_CreateRGBSurface(0, 8, font->h, 32, rmask, gmask, bmask, amask)) == 0) {
        SDL_FreeSurface(surface);
        return 0;
    }
    
    // Render text
    dst.w = 8;
    dst.h = font->h;
    img = sd_rgba_image_create(8, font->h);
    for(int i = 0; i < slen; i++) {
        sd_font_decode(font, img, text[i] - 32, 64, 128, 64);
        memcpy(tmp->pixels, img->data, 4*8*font->h);
        dst.y = i / char_w * font->h;
        dst.x = i % char_w * 8;
        SDL_BlitSurface(tmp, 0, surface, &dst);
    }
    
    // All done.
    SDL_FreeSurface(tmp);
    sd_rgba_image_delete(img);
    return surface;
}
Example #2
0
int animation_create(animation *ani, sd_animation *sdani, sd_palette *pal, int overlay, char *soundtable) {
    ani->sdani = sdani;
    array_create(&ani->sprites);
    ani->soundtable = soundtable;

    // Load textures
    sd_rgba_image *img = 0;
    for(int i = 0; i < sdani->frame_count; i++) {
        img = sd_sprite_image_decode(sdani->sprites[i]->img, pal, overlay);
        texture *tex = malloc(sizeof(texture));
        texture_create(tex, img->data, img->w, img->h);
        array_set(&ani->sprites, i, tex);
        sd_rgba_image_delete(img);
    }
    return 0;
}
Example #3
0
int font_load(font *font, const char* filename, unsigned int size) {
    sd_rgba_image *img;
    sd_font *sdfont;
    int pixsize;
    texture *tex;
    
    // Find vertical size
    switch(size) {
        case FONT_BIG: pixsize = 8; break;
        case FONT_SMALL: pixsize = 6; break;
        default:
            return 1;
    }
    
    // Open font file
    sdfont = sd_font_create();
    if(sd_font_load(sdfont, filename, pixsize)) {
        sd_font_delete(sdfont);
        return 2;
    }
    
    // Load into textures
    img = sd_rgba_image_create(pixsize, pixsize);
    for(int i = 0; i < 224; i++) {
        tex = malloc(sizeof(texture));
        sd_font_decode(sdfont, img, i, 0xFF, 0xFF, 0xFF);
        texture_create(tex);
        texture_init(tex, img->data, img->w, img->h);
        vector_append(&font->textures, &tex);
    }
    
    // Set font info vars
    font->w = pixsize;
    font->h = pixsize;
    font->size = size;
    
    // Free resources
    sd_rgba_image_delete(img);
    sd_font_delete(sdfont);
    return 0;
}
Example #4
0
// extract part of a sprite as a new sprite
// we need this because the HAR portraits are one single sprite, unlike the player portraits
// so we need to chunk them up into individual sprites and strip out the black background
sd_rgba_image* sub_sprite(sd_sprite *sprite, sd_palette *pal, int x, int y, int w, int h) {
    sd_rgba_image *img = 0;
    sd_rgba_image *out = sd_rgba_image_create(w, h);
    img = sd_sprite_image_decode(sprite->img, pal, -1);
    for(int i = y; i < y+h; i++) {
        for(int j = x; j < x+w; j++) {
            int offset = (i*sprite->img->w*4)+(j*4);
            int local_offset = ((i-y)*w*4)+((j-x)*4);
            out->data[local_offset]   = (char)img->data[offset];
            out->data[local_offset+1] = (char)img->data[offset+1];
            out->data[local_offset+2] = (char)img->data[offset+2];
            if (!out->data[local_offset] && !out->data[local_offset+1] && !out->data[local_offset+2]) {
                // all three colors are black, set the pixel to be transparent!
                out->data[local_offset+3] = 0;
            } else {
                out->data[local_offset+3] = (char)img->data[offset+3];
            }
        }
    }
    sd_rgba_image_delete(img);
    return out;
}
Example #5
0
int melee_init(scene *scene) {
    char bitmap[51*36*4];
    memset(&bitmap, 255, 51*36*4);
    ticks = 0;
    pulsedir = 0;
    selection = 0;
    row_a = 0;
    column_a = 0;
    row_b = 0;
    column_b = 4;
    done_a = 0;
    done_b = 0;
    for(int i = 0; i < 10; i++) {
        players[i].sprite = scene->bk->anims[3]->animation->sprites[i];
        DEBUG("found sprite %d x %d at %d, %d", players[i].sprite->img->w, players[i].sprite->img->h, players[i].sprite->pos_x, players[i].sprite->pos_y);
        players_big[i].sprite = scene->bk->anims[4]->animation->sprites[i];

        int row = i / 5;
        int col = i % 5;
        sd_rgba_image * out = sub_sprite(scene->bk->anims[1]->animation->sprites[0], scene->bk->palettes[0], (62*col), (42*row), 51, 36);
        texture_create(&harportraits[i], out->data, 51, 36);
        sd_rgba_image_delete(out);
    }
    menu_background2_create(&feh, 90, 61);
    menu_background2_create(&bleh, 160, 43);
    texture_create(&select_hilight, bitmap, 51, 36);

    // set up the magic controller hooks
    if (scene->player1.ctrl->type == CTRL_TYPE_NETWORK) {
        controller_add_hook(scene->player2.ctrl, scene->player1.ctrl, scene->player1.ctrl->controller_hook);
    }

    if (scene->player2.ctrl->type == CTRL_TYPE_NETWORK) {
        controller_add_hook(scene->player1.ctrl, scene->player2.ctrl, scene->player2.ctrl->controller_hook);
    }


    const color bar_color = color_create(0, 190, 0, 255);
    const color bar_bg_color = color_create(80, 220, 80, 0);
    const color bar_border_color = color_create(0, 96, 0, 255);
    const color bar_top_left_border_color = color_create(0, 255, 0, 255);
    const color bar_bottom_right_border_color = color_create(0, 125, 0, 255);
    progressbar_create(&bar_power[0],     74, 12, 20*4, 8, bar_border_color, bar_border_color, bar_bg_color, bar_top_left_border_color, bar_bottom_right_border_color, bar_color, PROGRESSBAR_LEFT);
    progressbar_create(&bar_agility[0],   74, 30, 20*4, 8, bar_border_color, bar_border_color, bar_bg_color, bar_top_left_border_color, bar_bottom_right_border_color, bar_color, PROGRESSBAR_LEFT);
    progressbar_create(&bar_endurance[0], 74, 48, 20*4, 8, bar_border_color, bar_border_color, bar_bg_color, bar_top_left_border_color, bar_bottom_right_border_color, bar_color, PROGRESSBAR_LEFT);
    progressbar_create(&bar_power[1],     320-66-feh.w, 12, 20*4, 8, bar_border_color, bar_border_color, bar_bg_color, bar_top_left_border_color, bar_bottom_right_border_color, bar_color, PROGRESSBAR_LEFT);
    progressbar_create(&bar_agility[1],   320-66-feh.w, 30, 20*4, 8, bar_border_color, bar_border_color, bar_bg_color, bar_top_left_border_color, bar_bottom_right_border_color, bar_color, PROGRESSBAR_LEFT);
    progressbar_create(&bar_endurance[1], 320-66-feh.w, 48, 20*4, 8, bar_border_color, bar_border_color, bar_bg_color, bar_top_left_border_color, bar_bottom_right_border_color, bar_color, PROGRESSBAR_LEFT);
    for(int i = 0;i < 2;i++) {
        progressbar_set(&bar_power[i], 50);
        progressbar_set(&bar_agility[i], 50);
        progressbar_set(&bar_endurance[i], 50);
    }
    refresh_pilot_stats();

    memset(&harplayer_a, 0, sizeof(harplayer_a));
    memset(&harplayer_b, 0, sizeof(harplayer_b));

    // All done
    return 0;
}
Example #6
0
void sprite_play(sd_bk_file *bk, int scale, int anim, int sprite) {
    if(!check_anim_sprite(bk, anim, sprite)) return;
    SDL_Surface *surface;
    SDL_Texture *texture;
    SDL_Texture *background;
    SDL_Texture *rendertarget;
    SDL_Rect rect;
    SDL_Rect dstrect;
    sd_sprite *s = bk->anims[anim]->animation->sprites[sprite];
    SDL_Window *window = SDL_CreateWindow(
            "OMF2097 Remake",
            SDL_WINDOWPOS_CENTERED,
            SDL_WINDOWPOS_CENTERED,
            320 * scale,
            200 * scale,
            SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL
            );

    if (!window) {
        printf("Could not create window: %s\n", SDL_GetError());
        return;
    }
    
    printf("Sprite Info: pos=(%d,%d) size=(%d,%d) len=%d\n", s->pos_x, s->pos_y, s->img->w, s->img->h, s->img->len);
    
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    
    uint32_t rmask, gmask, bmask, amask;

    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
    amask = 0xff000000;

    sd_rgba_image *img = sd_vga_image_decode(bk->background, bk->palettes[0], -1);

    if(!(surface = SDL_CreateRGBSurfaceFrom((void*)img->data, img->w, img->h, 32, img->w*4,
            rmask, gmask, bmask, amask))) {
        printf("Could not create surface: %s\n", SDL_GetError());
        return;
    }

    if ((background = SDL_CreateTextureFromSurface(renderer, surface)) == 0) {
        printf("Could not create texture: %s\n", SDL_GetError());
        return;
    }
    
    if((rendertarget = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 320, 200)) == 0) {
        printf("Could not create texture: %s\n", SDL_GetError());
        return;
    }

    SDL_FreeSurface(surface);
    sd_rgba_image_delete(img);

    img = sd_sprite_image_decode(s->img, bk->palettes[0], -1);

    if(!(surface = SDL_CreateRGBSurfaceFrom((void*)img->data, img->w, img->h, 32, img->w*4,
            rmask, gmask, bmask, amask))) {
        printf("Could not create surface: %s\n", SDL_GetError());
        return;
    }

    if ((texture = SDL_CreateTextureFromSurface(renderer, surface)) == 0) {
        printf("Could not create texture: %s\n", SDL_GetError());
        return;
    }

    SDL_FreeSurface(surface);
    sd_rgba_image_delete(img);

    rect.x = s->pos_x;
    rect.y = s->pos_y;
    rect.w = s->img->w;
    rect.h = s->img->h;
    
    dstrect.x = 0;
    dstrect.y = 0;
    dstrect.w = 320 * scale;
    dstrect.h = 200 * scale;

    while(1) {
        SDL_Event e;
        if (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                break;
            } else if (e.type == SDL_KEYUP) {
                int i = anim;
                int changed = 0;
                switch (e.key.keysym.sym) {
                    case SDLK_RIGHT:
                        sprite = (sprite+1) % bk->anims[anim]->animation->frame_count;
                        printf("sprite is now %u\n", sprite);
                        changed = 1;
                        break;
                    case SDLK_LEFT:
                        sprite--;
                        if (sprite < 0) {
                            sprite = bk->anims[anim]->animation->frame_count - 1;
                        }
                        changed = 1;
                        break;
                    case SDLK_UP:
                        i++;
                        while (!check_anim(bk, i) && i < 50) {
                            i++;
                        }
                        if (i == 50) {
                            printf("no more animations\n");
                        } else {
                            anim = i;
                            printf("UP: animation is now %u\n", anim);
                            sd_bk_anim *bka = bk->anims[anim];
                            sd_animation *ani = bka->animation;
                            bkanim_info(bka, ani, anim);
                            sprite = 0;
                        }
                        changed = 1;
                        break;
                    case SDLK_DOWN:
                        i--;
                        while (!check_anim(bk, i) && i >= 0) {
                            i--;
                        }
                        if (i < 0) {
                            printf("no previous animations\n");
                        } else {
                            anim = i;
                            printf("DOWN: animation is now %u\n", anim);
                            sd_bk_anim *bka = bk->anims[anim];
                            sd_animation *ani = bka->animation;
                            bkanim_info(bka, ani, anim);
                            sprite = 0;
                        }
                        changed = 1;
                        break;
                    default:
                        changed = 0;
                }
                if (changed) {
                    s = bk->anims[anim]->animation->sprites[sprite];
                    img = sd_sprite_image_decode(s->img, bk->palettes[0], -1);
                    int x = s->pos_x + bk->anims[anim]->animation->start_x;
                    int y = s->pos_y + bk->anims[anim]->animation->start_y;
                    printf("Sprite Info: pos=(%d,%d) size=(%d,%d) len=%d\n", x, y, s->img->w, s->img->h, s->img->len);

                    if(!(surface = SDL_CreateRGBSurfaceFrom((void*)img->data, img->w, img->h, 32, img->w*4,
                                    rmask, gmask, bmask, amask))) {
                        printf("Could not create surface: %s\n", SDL_GetError());
                        return;
                    }

                    if ((texture = SDL_CreateTextureFromSurface(renderer, surface)) == 0) {
                        printf("Could not create texture: %s\n", SDL_GetError());
                        return;
                    }

                    SDL_FreeSurface(surface);

                    rect.x = x;
                    rect.y = y;
                    rect.w = s->img->w;
                    rect.h = s->img->h;
                }
            }
        }
        SDL_RenderClear(renderer);
        SDL_SetRenderTarget(renderer, rendertarget);
        SDL_RenderCopy(renderer, background, NULL, NULL);
        SDL_RenderCopy(renderer, texture, NULL, &rect);

        // render the collision data
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        for(int i = 0; i < bk->anims[anim]->animation->col_coord_count; i++) {
            int x = bk->anims[anim]->animation->col_coord_table[i].x;
            int y = bk->anims[anim]->animation->col_coord_table[i].y;
            int y_ext = bk->anims[anim]->animation->col_coord_table[i].y_ext;
            if (y_ext == sprite) {
                SDL_RenderDrawPoint(renderer, x, y);
            }
        }

        SDL_SetRenderTarget(renderer, NULL);
        SDL_RenderCopy(renderer, rendertarget, NULL, &dstrect);
        SDL_RenderPresent(renderer);
        SDL_Delay(10); // don't chew too much CPU
    }

    // Close and destroy the window
    SDL_DestroyWindow(window);

    // Clean up
    SDL_Quit();
}
Example #7
0
// Loads BK file etc.
int scene_load(scene *scene, unsigned int scene_id) {
    scene->bk = sd_bk_create();
    scene->loop = 1;
    scene->local = NULL;
    int ret = 0;
    
    // Load BK
    switch(scene_id) {
        case SCENE_INTRO:    ret = sd_bk_load(scene->bk, "resources/INTRO.BK");    break;
        case SCENE_MENU:     ret = sd_bk_load(scene->bk, "resources/MAIN.BK");     break;
        case SCENE_ARENA0:   ret = sd_bk_load(scene->bk, "resources/ARENA0.BK");   break;
        case SCENE_ARENA1:   ret = sd_bk_load(scene->bk, "resources/ARENA1.BK");   break;
        case SCENE_ARENA2:   ret = sd_bk_load(scene->bk, "resources/ARENA2.BK");   break;
        case SCENE_ARENA3:   ret = sd_bk_load(scene->bk, "resources/ARENA3.BK");   break;
        case SCENE_ARENA4:   ret = sd_bk_load(scene->bk, "resources/ARENA4.BK");   break;
        case SCENE_ARENA5:   ret = sd_bk_load(scene->bk, "resources/ARENA5.BK");   break;
        case SCENE_NEWSROOM: ret = sd_bk_load(scene->bk, "resources/NEWSROOM.BK"); break;
        case SCENE_END:      ret = sd_bk_load(scene->bk, "resources/END.BK");      break;
        case SCENE_END1:     ret = sd_bk_load(scene->bk, "resources/END1.BK");     break;
        case SCENE_END2:     ret = sd_bk_load(scene->bk, "resources/END2.BK");     break;
        case SCENE_CREDITS:  ret = sd_bk_load(scene->bk, "resources/CREDITS.BK");  break;
        case SCENE_MECHLAB:  ret = sd_bk_load(scene->bk, "resources/MECHLAB.BK");  break;
        case SCENE_MELEE:    ret = sd_bk_load(scene->bk, "resources/MELEE.BK");    break;
        case SCENE_VS:       ret = sd_bk_load(scene->bk, "resources/VS.BK");       break;
        case SCENE_NORTHAM:  ret = sd_bk_load(scene->bk, "resources/NORTH_AM.BK"); break;
        case SCENE_KATUSHAI: ret = sd_bk_load(scene->bk, "resources/KATUSHAI.BK"); break;
        case SCENE_WAR:      ret = sd_bk_load(scene->bk, "resources/WAR.BK");      break;
        case SCENE_WORLD:    ret = sd_bk_load(scene->bk, "resources/WORLD.BK");    break;
        default:
            sd_bk_delete(scene->bk);
            PERROR("Unknown scene_id!");
            return 1;
    }
    if(ret) {
        sd_bk_delete(scene->bk);
        PERROR("Unable to load BK file!");
        return 1;
    }

    scene->this_id = scene_id;
    scene->next_id = scene_id;

    // optional callback
    scene->post_init = NULL;
    
    // Load specific stuff
    switch(scene_id) {
        case SCENE_INTRO: intro_load(scene); break;
        case SCENE_MENU: mainmenu_load(scene); break;
        case SCENE_CREDITS: credits_load(scene); break;
        case SCENE_MELEE:
            fixup_palette(scene->bk->palettes[0]);
            melee_load(scene); break;
        case SCENE_VS:
            fixup_palette(scene->bk->palettes[0]);
            vs_load(scene); break;
        case SCENE_MECHLAB:
            mechlab_load(scene);
            break;
        case SCENE_ARENA0:
        case SCENE_ARENA1:
        case SCENE_ARENA2:
        case SCENE_ARENA3:
        case SCENE_ARENA4:
        case SCENE_ARENA5:
            fixup_palette(scene->bk->palettes[0]);
            arena_load(scene); 
            break;
            
        default: 
            scene->render = NULL;
            scene->event = NULL;
            scene->init = NULL;
            scene->deinit = NULL;
    }
    
    // Init scene
    if(scene->init != NULL) {
        if(scene->init(scene)) {
            sd_bk_delete(scene->bk);
            return 1;
        }
    }
    
    // Convert background
    sd_rgba_image *bg = sd_vga_image_decode(scene->bk->background, scene->bk->palettes[0], -1);
    texture_create(&scene->background, bg->data, bg->w, bg->h);
    sd_rgba_image_delete(bg);
    
    // Players list
    list_create(&scene->child_players);
    list_create(&scene->root_players);
    
    // Handle animations
    animation *ani;
    sd_bk_anim *bka;
    array_create(&scene->animations);
    for(unsigned int i = 0; i < 50; i++) {
        bka = scene->bk->anims[i];
        if(bka) {
            // Create animation + textures, etc.
            ani = malloc(sizeof(animation));
            animation_create(ani, bka->animation, scene->bk->palettes[0], -1, scene->bk->soundtable);
            array_set(&scene->animations, i, ani);
            
            // Start playback on those animations, that have load_on_start flag as true 
            // or if we are handling animation 25 of intro
            // TODO: Maybe make the exceptions a bit more generic or something ?
            // TODO check other probabilites here
            if(bka->load_on_start || bka->probability == 1 || (scene_id == SCENE_INTRO && i == 25)) {
                animationplayer player;
                player.x = ani->sdani->start_x;
                player.y = ani->sdani->start_y;
                animationplayer_create(&player, i, ani);
                player.userdata = scene;
                player.add_player = scene_add_ani_player;
                player.del_player = scene_set_ani_finished;
                list_append(&scene->root_players, &player, sizeof(animationplayer));
                DEBUG("Create animation %d @ x,y = %d,%d", i, player.x, player.y);
            }
        }
    }

    // run post init, if defined
    if(scene->post_init != NULL) {
        DEBUG("running post init");
        scene->post_init(scene);
    }

    // All done
    DEBUG("Scene %i loaded! Textures now using %d bytes of (v)ram!", scene_id, texturelist_get_bsize());
    return 0;
}
Example #8
0
int main(int argc, char *argv[]) {
    // commandline argument parser options
    struct arg_lit *help = arg_lit0("h", "help", "print this help and exit");
    struct arg_lit *vers = arg_lit0("v", "version", "print version information and exit");
    struct arg_file *file = arg_file1("f", "file", "<file>", "Input .BK file");
    struct arg_str *outdir = arg_str1("o", "outdir", "<str>", "Output directory");
    struct arg_str *name = arg_str1("n", "name", "<str>", "Output name");
    struct arg_end *end = arg_end(20);
    void* argtable[] = {help,vers,file,outdir,name,end};
    const char* progname = "bkhtmlprinter";

    // Make sure everything got allocated
    if(arg_nullcheck(argtable) != 0) {
        printf("%s: insufficient memory\n", progname);
        goto exit_0;
    }

    // Parse arguments
    int nerrors = arg_parse(argc, argv, argtable);

    // Handle help
    if(help->count > 0) {
        printf("Usage: %s", progname);
        arg_print_syntax(stdout, argtable, "\n");
        printf("\nArguments:\n");
        arg_print_glossary(stdout, argtable, "%-30s %s\n");
        goto exit_0;
    }

    // Handle version
    if(vers->count > 0) {
        printf("%s v0.1\n", progname);
        printf("Command line One Must Fall 2097 .BK html printer.\n");
        printf("Source code is available at https://github.com/omf2097 under MIT license.\n");
        printf("(C) 2013 Tuomas Virtanen\n");
        goto exit_0;
    }

    // Handle errors
    if(nerrors > 0) {
        arg_print_errors(stdout, end, progname);
        printf("Try '%s --help' for more information.\n", progname);
        goto exit_0;
    }

    // Load file
    sd_bk_file *bk = sd_bk_create();
    int ret = sd_bk_load(bk, file->filename[0]);
    if(ret) {
        printf("Unable to load BK file! Make sure the file exists and is a valid BK file.\n");
        goto exit_1;
    }

    // Some vars
    FILE *fp;
    char namebuf[256];

    // Open output file
    FILE *f;
    sprintf(namebuf, "%s/%s.html", outdir->sval[0], name->sval[0]);
    f = fopen(namebuf, "w");
    if(f == NULL) {
        printf("Error while opening file!");
        goto exit_1;
    }

    // Write background
    sprintf(namebuf, "%s/%s_bg.png", outdir->sval[0], name->sval[0]);
    fp = fopen(namebuf, "wb");
    if(f == NULL) {
        printf("Error while opening background file for writing!");
        goto exit_1;
    }
    sd_rgba_image *bg = sd_vga_image_decode(bk->background, bk->palettes[0], 0);
    write_png(fp, bg->data, bg->w, bg->h);
    sd_rgba_image_delete(bg);
    fclose(fp);

    // Print header to file
    fprintf(f, "%s", header);
    fprintf(f, "<h1>%s</h1>", file->filename[0]);

    // Root
    fprintf(f, "<h2>General information</h2><table><tr><th>Key</th><th>Value</th></tr>");
    fprintf(f, "<tr><td>File ID</td><td>%d</td></tr>", bk->file_id);
    fprintf(f, "</table>");

    // Image
    fprintf(f, "<h2>Background</h2>");
    sprintf(namebuf, "%s_bg.png", name->sval[0]);
    fprintf(f, "<img src=\"%s\" width=\"640\" height=\"400\" />", namebuf);

    // Palettes
    if(bk->num_palettes > 0) {
        fprintf(f, "<h2>Palettes</h2>");
        for(int i = 0; i < bk->num_palettes; i++) {
            sd_palette *pal = bk->palettes[i];
            fprintf(f, "<h3>Palette %d</h3>", i+1);
            fprintf(f, "<table>");
            for(int y = 0; y < 16; y++) {
                fprintf(f, "<tr>");
                for(int x = 0; x < 16; x++) {
                    fprintf(f, "<td style=\"background-color: rgb(%d,%d,%d); text-align: middle; width: 30px; height: 30px; color: white;\">%d</td>",
                            pal->data[y*16+x][0],
                            pal->data[y*16+x][1],
                            pal->data[y*16+x][2],
                            y*16 + x);
                }
                fprintf(f, "</tr>");
            }
            fprintf(f, "</table>");
        }
    }

    // Animations
    fprintf(f, "<h2>Animations</h2><div id=\"animations\">");
    for(int m = 0; m < 50; m++) {
        if(bk->anims[m]) {
            sd_bk_anim *bka = bk->anims[m];
            sd_animation *ani = bka->animation;
            fprintf(f, "<h3>Animation %d</h3><div class=\"animation\">", m);
            fprintf(f, "<div class=\"iblock\"><h4>General information</h4>");
            fprintf(f, "<table><tr><th>Key</th><th>Value</th></tr>");
            fprintf(f, "<tr><td>Null</td><td>%d</td></tr>", bka->null);
            fprintf(f, "<tr><td>Chain if hit</td><td>%d</td></tr>", bka->chain_hit);
            fprintf(f, "<tr><td>Chain if not hit</td><td>%d</td></tr>", bka->chain_no_hit);
            fprintf(f, "<tr><td>Load on start</td><td>%d</td></tr>", bka->load_on_start);
            fprintf(f, "<tr><td>Probability</td><td>%d</td></tr>", bka->probability);
            fprintf(f, "<tr><td>Hazard damage</td><td>%d</td></tr>", bka->hazard_damage);
            fprintf(f, "<tr><td>Unknown</td><td>%s</td></tr>", bka->unknown_data);

            fprintf(f, "<tr><td>Start X</td><td>%d</td></tr>", ani->start_x);
            fprintf(f, "<tr><td>Start Y</td><td>%d</td></tr>", ani->start_y);
            fprintf(f, "<tr><td>Animation string</td><td>%s</td></tr>", ani->anim_string);
            fprintf(f, "</table></div>");

            // Extra strings
            if(ani->extra_string_count) {
                fprintf(f, "<div class=\"iblock\"><h4>Extra strings</h4>");
                fprintf(f, "<table><tr><th>#</th><th>String</th></tr>");
                for(int e = 0; e < ani->extra_string_count; e++) {
                    fprintf(f, "<tr><td>%d</td><td>%s</td></tr>", e, ani->extra_strings[e]);
                }
                fprintf(f, "</table></div>");
            }

            // Coords
            if(ani->col_coord_count > 0) {
                fprintf(f, "<div class=\"iblock\"><h4>Collision coordinates</h4>");
                fprintf(f, "<table><tr><th>X</th><th>Y</th><th>X-ext</th><th>Y-ext</th></tr>");
                for(int c = 0; c < ani->col_coord_count; c++) {
                    col_coord *coord = &ani->col_coord_table[c];
                    fprintf(f, "<tr><td>%d</td><td>%d</td><td>%d</td><td>%d</td></tr>", coord->x, coord->y, coord->x_ext, coord->y_ext);
                }
                fprintf(f, "</table></div>");
            }

            // Frames
            fprintf(f, "<div class=\"iblock\"><h4>Frames</h4>");
            fprintf(f, "<table><tr><th>#</th><th>X</th><th>Y</th><th>W</th><th>H</th><th>Index</th><th>Missing</th><th>Sprite</th></tr>");
            for(int b = 0; b < ani->frame_count; b++) {
                sd_sprite *sprite = ani->sprites[b];

                // Write sprite
                if(sprite->img->len > 0 && sprite->img->w > 0 && sprite->img->h > 0) {
                    sprintf(namebuf, "%s/%s_sprite_%d_%d.png", outdir->sval[0], name->sval[0], m, b);
                    fp = fopen(namebuf, "wb");
                    sd_rgba_image *img = sd_sprite_image_decode(sprite->img, bk->palettes[0], 0);
                    write_png(fp, img->data, img->w, img->h);
                    sd_rgba_image_delete(img);
                    fclose(fp);
                    sprintf(namebuf, "%s_sprite_%d_%d.png", name->sval[0], m, b);
                } else {
                    namebuf[0] = 0;
                }

                // Print html
                fprintf(f, "<tr><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td><img src=\"%s\" /></td></tr>",
                        b,
                        sprite->pos_x,
                        sprite->pos_y,
                        sprite->img->w,
                        sprite->img->h,
                        sprite->index,
                        sprite->missing,
                        namebuf);
            }
            fprintf(f, "</table>");

            fprintf(f, "</div></div>");
        }
    }
    fprintf(f, "</div>");

    // Sounds
    fprintf(f, "<h2>Sound table</h2><table><tr><th>Local ID</th><th>Sound ID</th></tr>");
    for(int i = 0; i < 30; i++) {
        fprintf(f, "<tr><td>%d</td><td>%d</td></tr>", i, (int)bk->soundtable[i]);
    }
    fprintf(f, "</table>");


    // Print footer to file
    fprintf(f, "%s", footer);

    // Quit
    fclose(f);
exit_1:
    sd_bk_delete(bk);
exit_0:
    arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
    return 0;
}