Beispiel #1
0
void scene_free(scene *scene) {
    if(!scene) return;
    
    // Deinit scene
    if(scene->deinit != NULL) {
        scene->deinit(scene);
    }
    
    // Release background
    texture_free(&scene->background);
    
    // Free players
    iterator it;
    animationplayer *tmp = 0;
    list_iter_begin(&scene->child_players, &it);
    while((tmp = iter_next(&it)) != NULL) {
        animationplayer_free(tmp);
    }
    list_iter_begin(&scene->root_players, &it);
    while((tmp = iter_next(&it)) != NULL) {
        animationplayer_free(tmp);
    }
    list_free(&scene->child_players);
    list_free(&scene->root_players);
    
    // Free animations
    animation *ani = 0;
    array_iter_begin(&scene->animations, &it);
    while((ani = iter_next(&it)) != 0) {
        animation_free(ani);
        free(ani);
    }
    array_free(&scene->animations);

    // XXX do NOT free hars/controllers here!

    // Free BK
    sd_bk_delete(scene->bk);
}
Beispiel #2
0
void test_sd_bk_delete(void) {
    sd_bk_delete(bk_file);
    //CU_ASSERT_PTR_NULL(bk_file);
}
Beispiel #3
0
int main(int argc, char **argv) {
    char buf[256];
    char *ext;

    if (argc <= 1) {
        printf("Usage %s [-p palette]  <filename>\n", argv[0]);
        return 1;
    }

    strncpy(buf, argv[argc-1], 255);
    buf[255] = '0';
    basename(buf);

    ext = strrchr(buf, '.');
    if (ext == NULL) {
        printf("cannot determine file extension for %s\n", buf);
        return 1;
    }

    if (strncmp(ext, ".AF", 3) == 0) {
        if (argc < 3 || strncmp(argv[1], "-p", 2)) {
            printf("AF files need a corresponding palette, please supply one with -p (eg ARENA0.BK)\n");
            return 1;
        }

        sd_bk_file *bk_file;
        sd_af_file *file;
        printf("Loading AF file: %s\n", argv[argc-1]);
        file = sd_af_load(argv[argc-1]);
        if (file) {
            printf("File loaded.\n");
            printf("Reading palette from %s\n", argv[2]);
            bk_file = sd_bk_load(argv[2]);
            if (bk_file) {
                printf("Palette loaded.\n");
                for(int i = 0; i < 50; i++) {
                    if (file->moves[i]) {
                        print_sprites(i, &file->moves[i]->animation, bk_file->palettes[0]);
                    }
                }
                printf("Destroying resources ...\n");
                sd_bk_delete(bk_file);
                sd_af_delete(file);
            } else {
                printf("Unable to load palette from file.\n");
                return 1;
            }
        } else {
            printf("Unable to load file!\n");
            return 1;
        }
        return 0;
    } else if (strncmp(ext, ".BK", 3) == 0) {
        sd_bk_file *file;
        printf("Loading BK file: %s\n", argv[1]);
        file = sd_bk_load(argv[1]);
        if(file) {
            printf("File loaded.\n");
            printf("ID: %d\n", file->file_id);
            for (int i = 0; i < file->num_palettes; i++) {
                printf("drawing background with pallete %d to background-%d.ppm\n", i, i);
                sprintf(buf, "background-%d.ppm", i);
                sd_rgba_image_to_ppm(sd_vga_image_decode(file->background, file->palettes[i], -1), buf);
            }

            for(int i = 0; i < 50; i++) {
                if (file->animations[i]) {
                    print_sprites(i, file->animations[i], file->palettes[0]);
                } else {
                    /*printf("skipping blank animation %d\n", i);*/
                }
            }

            printf("Destroying resources ...\n");
            sd_bk_delete(file);
        } else {
            printf("Unable to load file!\n");
        }
    } else {
        printf("Unrecognized file extenion %s\n", ext);
        return 1;
    }

    printf("Exiting.\n");
    return 0;
}
Beispiel #4
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_file *output = arg_file0("o", "output", "<file>", "Output .BK file");
    struct arg_int *anim = arg_int0("a", "anim", "<animation_id>", "Select animation");
    struct arg_lit *all_anims = arg_lit0("A", "all_anims", "All animations");
    struct arg_int *sprite = arg_int0("s", "sprite", "<sprite_id>", "Select sprite (requires --anim)");
    struct arg_lit *keylist = arg_lit0(NULL, "keylist", "Prints a list of valid fields for --key.");
    struct arg_str *key = arg_strn("k", "key", "<key>", 0, 2, "Select key");
    struct arg_str *value = arg_str0(NULL, "value", "<value>", "Set value (requires --key)");
    struct arg_lit *play = arg_lit0(NULL, "play", "Play animation or sprite (requires --anim)");
    struct arg_int *scale = arg_int0(NULL, "scale", "<factor>", "Scales sprites (requires --play)");
    struct arg_lit *parse = arg_lit0(NULL, "parse", "Parse value (requires --key)");
    struct arg_end *end = arg_end(20);
    void* argtable[] = {help,vers,file,output,anim,all_anims,sprite,keylist,key,value,play,scale,parse,end};
    const char* progname = "bktool";
    
    // 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 file editor.\n");
        printf("Source code is available at https://github.com/omf2097 under MIT license.\n");
        printf("(C) 2013 Tuomas Virtanen\n");
        goto exit_0;
    }
    
    // Argument dependencies
    if(anim->count == 0) {
        if(sprite->count > 0) {
            printf("--sprite requires --anim\n");
            printf("Try '%s --help' for more information.\n", progname);
            goto exit_0;
        }
        if(play->count > 0) {
            printf("--play requires --anim\n");
            printf("Try '%s --help' for more information.\n", progname);
            goto exit_0;
        }
    }
    if(key->count == 0) {
        if(value->count > 0) {
            printf("--value requires --key\n");
            printf("Try '%s --help' for more information.\n", progname);
            goto exit_0;
        }
    }
    if(output->count == 0) {
        if(value->count > 0) {
            printf("--value requires --output\n");
            printf("Try '%s --help' for more information.\n", progname);
            goto exit_0;
        }
    }
    if(play->count == 0) {
        if(scale->count > 0) {
            printf("--scale requires --play\n");
            printf("Try '%s --help' for more information.\n", progname);
            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;
    }
    
    // Init SDL
    SDL_Init(SDL_INIT_VIDEO);
    
    // 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;
    }
    
    // Scaling variable
    int _sc = 1;
    if(scale->count > 0) {
        _sc = scale->ival[0];
        if(_sc > 4) _sc = 4;
        if(_sc < 1) _sc = 1;
    }
    
    // Handle args
    if(sprite->count > 0) {
        // Make sure sprite exists.
        if(!check_anim_sprite(bk, anim->ival[0], sprite->ival[0])) {
            goto exit_1;
        }
        sd_sprite *sp = bk->anims[anim->ival[0]]->animation->sprites[sprite->ival[0]];
    
        // Handle arguments
        if(key->count > 0) {
            if(value->count > 0) {
                sprite_set_key(sp, key->sval, key->count, value->sval[0]);
            } else {
                sprite_get_key(sp, key->sval, key->count);
            }
        } else if(keylist->count > 0) {
            sprite_keylist();
        } else if(play->count > 0) {
            sprite_play(bk, _sc, anim->ival[0], sprite->ival[0]);
        } else {
            sprite_info(sp, anim->ival[0], sprite->ival[0]);
        }
    } else if(anim->count > 0) {
        // Make sure the bkanim exists
        if(!check_anim(bk, anim->ival[0])) {
            goto exit_1;
        }
        sd_bk_anim *bka = bk->anims[anim->ival[0]];
        sd_animation *ani = bka->animation;
    
        if(key->count > 0) {
            if(value->count > 0) {
                bkanim_set_key(bka, ani, key->sval, key->count, value->sval[0]);
            } else {
                bkanim_get_key(bka, ani, key->sval, key->count, parse->count);
            }
        } else if(keylist->count > 0) {
            bkanim_keylist();
        } else if(play->count > 0) {
            anim_play(bk, _sc, anim->ival[0]);
        } else {
            bkanim_info(bka, ani, anim->ival[0]);
        }
    } else if(all_anims->count > 0) {
        sd_bk_anim *bka;
        sd_animation *ani;
        for(int i = 0; i < 50; i++) {
            if (bk->anims[i]) {
                bka = bk->anims[i];
                ani = bka->animation;
                if(key->count > 0) {
                    if(value->count > 0) {
                        bkanim_set_key(bka, ani, key->sval, key->count, value->sval[0]);
                    } else {
                        printf("Animation %2u: ", i);
                        bkanim_get_key(bka, ani, key->sval, key->count, parse->count);
                    }
                } else {
                    printf("\n");
                    bkanim_info(bka, ani, i);
                }
            }
        }
    } else {
        if(key->count > 0) {
            if(value->count > 0) {
                bk_set_key(bk, key->sval, key->count, value->sval[0]);
            } else {
                bk_get_key(bk, key->sval, key->count);
            }
        } else if(keylist->count > 0) {
            bk_keylist();
        } else {
            bk_info(bk);
        }
    }
    
    // Write output file
    if(output->count > 0) {
        sd_bk_save(bk, output->filename[0]);
    }
    
    // Quit
exit_1:
    sd_bk_delete(bk);
    SDL_Quit();
exit_0:
    arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
    return 0;
}
Beispiel #5
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;
}
Beispiel #6
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;
}