掌機 - TRIMUI - 如何讓GNGEO支援GNO檔案格式



其實GNGEO模擬器本身是支援GNO檔案格式,只是不知道為何在載入ROM的時候,要做剔除副檔名的動作(src/main.c)

original_rom_name = cf_parse_cmd_line(argc,argv);
printf("original rom name=[%s]\n",original_rom_name);
rom_name = remove_path_and_extension(original_rom_name, '.', '/');
printf("rom name=[%s]\n",rom_name);

但是,後續處理ROM時卻又判斷是否有.GNO檔案

int load_game_config(char *rom_name) {
    char *gpath;
    char *drconf;
#ifdef EMBEDDED_FS
    gpath = ROOTPATH"conf/";
#else
    gpath = get_gngeo_dir();
#endif
    cf_reset_to_default();
    cf_open_file(NULL); /* Reset possible previous setting */

    if (rom_name) {
        if (strstr(rom_name,".gno") != NULL) {
            char *name = dr_gno_romname(rom_name);
                if (name) {
                    printf("Tring to load a gno file %s %s\n",rom_name,name);

因此,司徒在一開始先判斷是否為.GNO副檔名,如果不是,才做剔除的動作

char *rom_name = NULL;
char *ext_name = NULL;

cf_init();
cf_init_cmd_line();
cf_open_file(NULL);
rom_name = cf_parse_cmd_line(argc, argv);
if(rom_name){
    ext_name = strrchr(rom_name, '.');
    printf("rom name: %s\n", rom_name);
    if (strcasecmp(ext_name, ".gno")) {
        rom_name = remove_path_and_extension(rom_name, '.', '/');
    }
}

那接下來的問題是,什麼是GNO檔案呢?(src/rom.c)

int dr_save_gno(GAME_ROMS *r, char *filename)
{
    FILE *gno;
    char *fid = "gnodmpv1";
    char fname[9];
    Uint8 nb_sec = 0;
    int i;

    gno = fopen(filename, "wb");
    if (!gno)
        return GN_FALSE;

    /* restore game vector */
    memcpy(memory.rom.cpu_m68k.p, memory.game_vector, 0x80);
    for (i = 0; i < 0x80; i++)
        printf("%02x ", memory.rom.cpu_m68k.p[i]);
    printf("\n");

    if (r->cpu_m68k.p)
        nb_sec++;
    if (r->cpu_z80.p)
        nb_sec++;
    if (r->adpcma.p)
        nb_sec++;
    if (r->adpcmb.p && (r->adpcmb.p != r->adpcma.p))
        nb_sec++;
    if (r->game_sfix.p)
        nb_sec++;
    if (r->tiles.p)
        nb_sec += 2; /* Sprite + Sprite usage */
    if (r->gfix_usage.p)
        nb_sec++;
    /* Do we need Custom Bios? */
    if ((r->info.flags & HAS_CUSTOM_CPU_BIOS))
    {
        nb_sec++;
    }
    if ((r->info.flags & HAS_CUSTOM_SFIX_BIOS))
    {
        nb_sec++;
    }

    /* Header information */
    fwrite(fid, 8, 1, gno);
    snprintf(fname, 9, "%-8s", r->info.name);
    fwrite(fname, 8, 1, gno);
    fwrite(&r->info.flags, sizeof(Uint32), 1, gno);
    fwrite(&nb_sec, sizeof(Uint8), 1, gno);

    /* Now each section */
    dump_region(gno, &r->cpu_m68k, REGION_MAIN_CPU_CARTRIDGE, 0, 0);
    dump_region(gno, &r->cpu_z80, REGION_AUDIO_CPU_CARTRIDGE, 0, 0);
    dump_region(gno, &r->adpcma, REGION_AUDIO_DATA_1, 0, 0);
    if (r->adpcma.p != r->adpcmb.p)
        dump_region(gno, &r->adpcmb, REGION_AUDIO_DATA_2, 0, 0);
    dump_region(gno, &r->game_sfix, REGION_FIXED_LAYER_CARTRIDGE, 0, 0);
    dump_region(gno, &r->spr_usage, REGION_SPR_USAGE, 0, 0);
    dump_region(gno, &r->gfix_usage, REGION_GAME_FIX_USAGE, 0, 0);
    if ((r->info.flags & HAS_CUSTOM_CPU_BIOS))
    {
        dump_region(gno, &r->bios_m68k, REGION_MAIN_CPU_BIOS, 0, 0);
    }
    if ((r->info.flags & HAS_CUSTOM_SFIX_BIOS))
    {
        dump_region(gno, &r->bios_sfix, REGION_FIXED_LAYER_BIOS, 0, 0);
    }
    /* TODO, there is a bug in the loading routine, only one compressed (type 1)
     * region can be present at the end of the file */
    dump_region(gno, &r->tiles, REGION_SPRITES, 1, 4096);

    fclose(gno);
    return GN_TRUE;
}

P.S. 其實就是儲存已經解完密的每個REGION資料,所以小橫米、TRIMUI掌機應該要使用這種格式

那另外一個問題是,如何DUMP呢?(src/main.c)

/* If asked, do a .gno dump and exit*/
if (CF_BOOL(cf_get_item_by_name("dump"))) {
    char dump[8+4+1];
    sprintf(dump,"%s.gno",rom_name);
    dr_save_gno(&memory.rom,dump);
    close_game();
    return 0;
}

P.S. 只要在啟動gngeo時,使用--dump就可以做DUMP的動作

如下

$ ./gngeo --dump


P.S. 載入遊戲後,gngeo會自動離開並且產生(null).gno檔案,因為char dump並沒有被初始化

司徒使用小橫米、TRIMUI對比了一下ZIP和GNO的載入速度


司徒把沒有用到的選項都刪除了


FPS顯示還包含CPU使用率


CPU使用率計算方式

static int __attribute__((noinline)) get_cpu_ticks(void)
{
    static int fd = 0;
    static unsigned long last_utime = 0;

    char buf[128] = {0};
    unsigned long utime = 0, ret = 0;

    if (fd == 0)
    {
        fd = open("/proc/self/stat", O_RDONLY);
    }
    lseek(fd, 0, SEEK_SET);
    buf[0] = 0;
    read(fd, buf, sizeof(buf));
    buf[sizeof(buf) - 1] = 0;

    sscanf(buf, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu", &utime);
    ret = utime - last_utime;
    if (ret > 200)
    {
        ret = 0;
    }
    last_utime = utime;
    return ret;
}