/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* * GXMame * * Copyright 2002-2005 Stephane Pontier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "game_list.h" #include #include #include "io.h" #include "gui.h" #define LINE_BUF 512 /* Separator for game list fields. * This avoids problems when source * files are saved in different encodings. */ #define SEP "\xAC" /** * Inserts a string to a sorted glist if it's not present. * The function always returns a pointer to the string in the list. */ const gchar * glist_insert_unique (GList **list, const gchar *data) { GList *listpointer; gchar *data_copy; if (!data) return NULL; listpointer = g_list_first (*list); while ( (listpointer != NULL)) { if (!strcmp (listpointer->data, data)) return listpointer->data; listpointer = g_list_next (listpointer); } data_copy = g_strdup (data); *list = g_list_insert_sorted (*list, data_copy, (GCompareFunc)strcmp); return data_copy; } RomEntry * rom_entry_new (void) { RomEntry *rom; int i; rom = (RomEntry*) g_malloc0 (sizeof (RomEntry)); for (i = 0; i < NB_CPU; i++) { strcpy (rom->cpu_info[i].name, "-"); strcpy (rom->sound_info[i].name, "-"); } /* fill the some fields with default value if we have problems */ rom->timesplayed = 0; rom->has_roms = 2; rom->has_samples = 2; rom->favourite = FALSE; return rom; } void rom_entry_free (RomEntry *rom) { if (!rom) return; /* optimization: clonesort points to romname * if original. See: gamelist_add () */ if (rom->clonesort != rom->romname) g_free (rom->clonesort); g_free (rom->name_in_list); g_free (rom->gamename); g_free (rom->gamenameext); g_free (rom->manu); g_free (rom->cloneof); g_free (rom->romof); g_free (rom->sampleof); g_free (rom); } void rom_entry_set_driver (RomEntry *rom, const gchar *driver) { rom->driver = glist_insert_unique (&game_list.drivers, driver); } void rom_entry_set_year (RomEntry *rom, const gchar *year) { rom->year = glist_insert_unique (&game_list.years, year); } void rom_entry_set_name (RomEntry *rom, gchar *value) { char *p; if (!rom) return; if (!g_strncasecmp (value, "The ",4)) { value += 4; rom->the_trailer = TRUE; rom->name_in_list = NULL; } else { rom->name_in_list = g_strdup (value); } for (p = value; *p && (*p != '/') && (*p != '(');p++); if (*p == '/') { /*Fix for F/A (Japan)*/ if (!strncmp ((p - 1), "F/A", 3)) { rom->gamenameext = g_strdup (p + 2); *(p + 2) = '\0'; } else { rom->gamenameext = g_strdup (p + 1); *p = '\0'; } } else if (*p =='(') { rom->gamenameext = g_strdup (p); *p = 0; } if (!rom->gamenameext) rom->gamenameext = g_strdup (""); rom->gamename = g_strdup (value); } gchar ** rom_entry_get_manufacturers (RomEntry * rom) { gchar **manufacturer_fields; if (!rom->manu) return NULL; manufacturer_fields = g_strsplit (rom->manu, "]", 0); if (!manufacturer_fields[0]) return NULL; if (manufacturer_fields[1]) { /* we have two winners [Company 1] (company 2)*/ g_strstrip (g_strdelimit (manufacturer_fields[0], "[", ' ')); g_strstrip (g_strdelimit (manufacturer_fields[1], "()", ' ')); } else { g_strfreev (manufacturer_fields); manufacturer_fields = g_strsplit (rom->manu, "+", 0); if (manufacturer_fields[1] != NULL) { /* we have two winners Company1+Company2*/ g_strstrip (g_strdelimit (manufacturer_fields[0], "[", ' ')); g_strstrip (g_strdelimit (manufacturer_fields[1], "()", ' ')); } else { g_strfreev (manufacturer_fields); manufacturer_fields = g_strsplit (rom->manu, "/", 0); if (manufacturer_fields[1] != NULL) { /* we have two winners Company1/Company2*/ g_strstrip (g_strdelimit (manufacturer_fields[0], "[", ' ')); g_strstrip (g_strdelimit (manufacturer_fields[1], "()", ' ')); } else { g_strfreev (manufacturer_fields); manufacturer_fields = g_strsplit (rom->manu, "(", 0); if (manufacturer_fields[1] != NULL) { /* we have two winners Company1/Company2*/ g_strstrip (g_strdelimit (manufacturer_fields[0], "[", ' ')); g_strstrip (g_strdelimit (manufacturer_fields[1], "()", ' ')); } } } } return manufacturer_fields; } static gint compare_game_name (RomEntry *rom1, RomEntry *rom2) { return strcmp (rom1->clonesort, rom2->clonesort); } static FILE * gamelist_open (const char *mode) { FILE *handle; gchar *filename = g_build_filename (g_get_home_dir (), ".gxmame", "gamelist", NULL); handle = fopen (filename, mode); g_free (filename); return handle; } void gamelist_init (void) { memset (&game_list, 0, sizeof (GameList)); gui_prefs.current_game = NULL; } void gamelist_free (void) { if (game_list.name) g_free (game_list.name); if (game_list.version) g_free (game_list.version); if (game_list.roms) { g_list_foreach (game_list.roms, (GFunc) rom_entry_free, NULL); g_list_free (game_list.roms); } if (game_list.years) { g_list_foreach (game_list.years, (GFunc) g_free, NULL); g_list_free (game_list.years); } if (game_list.manufacturers) { g_list_foreach (game_list.manufacturers, (GFunc) g_free, NULL); g_list_free (game_list.manufacturers); } if (game_list.drivers) { g_list_foreach (game_list.drivers, (GFunc) g_free, NULL); g_list_free (game_list.drivers); } if (game_list.not_checked_list); g_list_free (game_list.not_checked_list); gamelist_init (); } #define FIELDS_PER_RECORD 23 + (NB_CPU * 4) /** * Adds a rom entry to the gamelist. * */ void gamelist_add (RomEntry *rom) { gchar **manufacturer_fields; int i; if (!rom->romname) { GXMAME_DEBUG ("Broken parser/gamelist loader. Romname was NULL."); } /*generate glist for manufacturers*/ manufacturer_fields = rom_entry_get_manufacturers (rom); if (manufacturer_fields) { for (i = 0; i < 2; i++) { if (manufacturer_fields[i]) { glist_insert_unique (&game_list.manufacturers, manufacturer_fields[i]); } } g_strfreev (manufacturer_fields); } if (!rom->cloneof) { rom->cloneof = g_strdup ("-"); } if (!rom->sampleof) { rom->sampleof = g_strdup ("-"); } if (!rom->romof) { rom->romof = g_strdup ("-"); } if (rom->cloneof[0] == '-') { /* original. Point to romname */ rom->clonesort = rom->romname; } else { rom->clonesort = g_strdup_printf ("%s-%s", rom->cloneof , rom->romname); } if (!rom->year) rom_entry_set_year (rom, _("Unknown")); if (!rom->control) strcpy (rom->control, "-"); game_list.roms = g_list_insert_sorted (game_list.roms, (gpointer) rom, (GCompareFunc )compare_game_name); game_list.num_games++; if (rom->nb_samples > 0) game_list.num_sample_games++; } /** * Loads the gamelist from the file. * * After calling this you must also call. * * load_games_ini (); * load_catver_ini (); * quick_check (); * create_filterslist_content (); * create_gamelist_content (); */ gboolean gamelist_load (void) { FILE *gamelist; gint romindex, j; gchar line[LINE_BUF]; gchar **tmp_array; gchar *tmp, *p; RomEntry *rom; gboolean exe_version_checked = FALSE; gint offset; int i; int supported_games = 0; romindex = 0; g_message (_("Loading gamelist")); gamelist_free (); game_list.version = NULL; game_list.name = NULL; gamelist = gamelist_open ("r"); if (!gamelist) { game_list.version = g_strdup ("unknown"); return FALSE; } while (fgets (line, LINE_BUF, gamelist)) { p = line; tmp = line; /* Skip comments */ if (*tmp != '#') { while (*tmp && (*tmp != '\n')) { tmp++; } *tmp = '\0'; tmp_array = g_strsplit (p, SEP, FIELDS_PER_RECORD); /* Check if the record is corrupted */ for (i=0; i < FIELDS_PER_RECORD; i++) { if (!tmp_array[i]) { g_strfreev (tmp_array); fclose (gamelist); game_list.version = g_strdup ("unknown"); gxmame_message (ERROR, NULL, _("Game list is corrupted.")); return FALSE; } } rom = rom_entry_new (); if (!rom || !tmp_array) { g_strfreev (tmp_array); fclose (gamelist); game_list.version = g_strdup ("unknown"); gxmame_message (ERROR, NULL, _("Out of memory while loading gamelist")); return FALSE; } g_strlcpy (rom->romname, tmp_array[0], MAX_ROMNAME); rom->gamename = g_strdup (tmp_array[1]); rom->gamenameext = g_strdup (tmp_array[2]); rom->the_trailer = !strcmp (tmp_array[3], "true"); rom_entry_set_year (rom, tmp_array[4]); rom->manu = g_strdup (tmp_array[5]); rom->cloneof = g_strdup (tmp_array[6]); rom->romof = g_strdup (tmp_array[7]); rom_entry_set_driver (rom, tmp_array[8]); if (!strcmp (tmp_array[9], "true")) rom->status = TRUE; else rom->status = FALSE; rom->colors = atoi (tmp_array[10]); /* offset of cpu infos in the array */ offset = 11; for (j = 0; j < NB_CPU; j++) { if (!strncmp (tmp_array[ (j * 2) + offset], "(sound)", 7)) { p = tmp_array[ (j * 2) + offset]; p += 7; g_snprintf (rom->cpu_info[j].name, MAX_CPU, "%s", p); rom->cpu_info[j].sound_flag = TRUE; } else { g_snprintf (rom->cpu_info[j].name, MAX_CPU, "%s", tmp_array[ (j * 2) + offset]); rom->cpu_info[j].sound_flag = FALSE; } rom->cpu_info[j].clock = atoi (tmp_array[ (j * 2) + offset + 1]); } /* calculate offset of sound cpu infos in the array */ offset = 11 + (NB_CPU * 2); for (j = 0; j < NB_CPU; j++) { if (strcmp (tmp_array[offset + (j * 2)], "")) { g_snprintf (rom->sound_info[j].name, MAX_CPU, "%s", tmp_array[offset + (j * 2)]); } rom->sound_info[j].clock = atoi (tmp_array[offset + (j * 2) + 1]); } offset = 11 + (NB_CPU * 4); rom->num_players = atoi (tmp_array[offset + 0]); rom->num_buttons = atoi (tmp_array[offset + 1]); if (strcmp (tmp_array[offset + 2], "")) { g_snprintf (rom->control, MAX_CONTROL, "%s", tmp_array[offset + 2]); } rom->vector = !strcmp (tmp_array[offset + 3], "true"); rom->screen_x = atoi (tmp_array[offset + 4]); rom->screen_y = atoi (tmp_array[offset + 5]); rom->screen_freq = atoi (tmp_array[offset + 6]); rom->horizontal = (*tmp_array[offset + 7] == 'h'); rom->channels = atoi (tmp_array[offset + 8]); rom->nb_roms = atoi (tmp_array[offset + 9]); rom->nb_samples = atoi (tmp_array[offset + 10]); rom->sampleof = g_strdup (tmp_array[offset + 11]); g_strfreev (tmp_array); gamelist_add (rom); supported_games++; } else if (!exe_version_checked) { /* Check for the GXMame version in the top comment line */ while (*tmp && (*tmp != '\n')) { tmp++; } *tmp = '\0'; tmp_array = g_strsplit (p, " ", 3); GXMAME_DEBUG ("Checking version of gamelist file: %s - %s", tmp_array[1], tmp_array[2]); if (strcmp (tmp_array[1], "GXMame") || !tmp_array[2]) { game_list.version = g_strdup ("unknown"); g_strfreev (tmp_array); fclose (gamelist); return FALSE; } if (g_ascii_strtod (tmp_array[2], NULL) < 0.7) { game_list.version = g_strdup ("too old"); g_strfreev (tmp_array); fclose (gamelist); return FALSE; } if (g_ascii_strtod (tmp_array[2], NULL) > 0.7) { game_list.version = g_strdup ("unknown"); g_strfreev (tmp_array); fclose (gamelist); return FALSE; } exe_version_checked = TRUE; g_strfreev (tmp_array); }else { while (*tmp && (*tmp != '\n')) { tmp++; } *tmp = '\0'; p += 2; /* Skip # */ if (!strncmp (p, "Version", 7)) { p += 8; game_list.version = g_strdup (p); } if (!strncmp (p, "Name", 4)) { p += 5; game_list.name = g_strdup (p); } } } fclose (gamelist); GXMAME_DEBUG ("List for %s %s", game_list.name, game_list.version); g_message (_("Loaded %d roms by %d manufacturers covering %d years."), game_list.num_games, g_list_length (game_list.manufacturers),g_list_length (game_list.years)); g_message (_("with %d games supporting samples."), game_list.num_sample_games); return (TRUE); } /** * Prints the gamelist "prefix" to the file. * Must be called once before printing any rom entries. */ static void gamelist_prefix_print (FILE *handle) { fprintf (handle, "# GXMame 0.7\n" "# Name %s\n" "# Version %s\n" "# list of xmame games for GXMame front-end\n" "# The fileformat is: " "romname" "gamename" "gamenameext" "the_trailer" "year" "manufacturer" "cloneof" "romof" "driverstatus" "colors" "cpu1" "cpu1_clock" "cpu2" "cpu2_clock" "cpu3" "cpu3_clock" "cpu4" "cpu4_clock" "sound1" "sound1_clock" "sound2" "sound2_clock" "sound3" "sound3_clock" "sound4" "sound4_clock" "num_players" "num_buttons" "control" "vector" "screen_x" "screen_y" "screen_freq" "horizontal" "channels" "num_roms" "num_samples" "sampleof\n", game_list.name, game_list.version ); } /** * Appends a rom entry to the gamelist. */ static void gamelist_print (FILE *handle, RomEntry *rom) { int i; char float_buf[FLOAT_BUF_SIZE]; if (!rom) return; fprintf (handle, "%s" SEP /* romname */ "%s" SEP /* gamename */ "%s" SEP /* gamenameext */ "%s" SEP /* the trailer */ "%s" SEP /* year */ "%s" SEP /* manu */ "%s" SEP /* clone of */ "%s" SEP /* rom of */ "%s" SEP /* driver */ "%s" SEP /* status */ "%i" SEP /* colors */ , rom->romname, rom->gamename, rom->gamenameext, rom->the_trailer ? "true" : "false", rom->year, rom->manu, rom->cloneof, rom->romof, rom->driver, rom->status ? "true" : "false", rom->colors ); for (i=0; i < NB_CPU; i++) { fprintf (handle, "%s" SEP "%i" SEP, rom->cpu_info[i].name, rom->cpu_info[i].clock); } for (i=0; i < NB_CPU; i++) { fprintf (handle, "%s" SEP "%i" SEP, rom->sound_info[i].name, rom->sound_info[i].clock); } fprintf (handle, "%i" SEP /* players */ "%i" SEP /* buttons */ "%s" SEP /* control */ "%s" SEP /* vector */ "%i" SEP /* screen X */ "%i" SEP /* screen Y */ "%s" SEP /* screen frequency */ "%s" SEP /* orientation */ "%i" SEP /* channels */ "%i" SEP /* roms */ "%i" SEP /* samples */ "\n", rom->num_players, rom->num_buttons, rom->control, rom->vector ? "true" : "false", rom->screen_x, rom->screen_y, my_dtostr (float_buf, rom->screen_freq), rom->horizontal ? "horizontal" : "vertical", rom->channels, rom->nb_roms, rom->nb_samples ); } /** * Saves the gamelist. */ gboolean gamelist_save (void) { GList *listpointer; FILE *gamelist; g_message (_("Saving gamelist.")); gamelist = gamelist_open ("w"); if (!gamelist) return FALSE; gamelist_prefix_print (gamelist); listpointer = g_list_first (game_list.roms); while (listpointer) { gamelist_print (gamelist, (RomEntry*)listpointer->data); listpointer = g_list_next (listpointer); } fclose (gamelist); return TRUE; } /** * Checks the gamelist and if its needs to be build/rebuild * it asks the user and proceeds accordingly. * * Checks: * - Gamelist was read but it was a version we don't support. * - Gamelist is not available. * - Gamelist was read but it was created with a very old version of gxmame. * - Gamelist does not match the current executable (and VersionCheck = TRUE) */ void gamelist_check (XmameExecutable *exec) { GtkWidget *dialog = NULL; gint result; if (!exec) return; if (!game_list.version || !strcmp (game_list.version,"unknown")) { dialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Could not recognise the gamelist version.\n" "Do you want to rebuild the gamelist?")); } else if (!strcmp (game_list.version,"none")) { dialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Gamelist not available,\n" "Do you want to build the gamelist?")); } else if (!strcmp (game_list.version,"too old")) { dialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Gamelist was created with an older version of GXMame.\n" "The gamelist is not supported.\n" "Do you want to rebuild the gamelist?")); } else if (gui_prefs.VersionCheck) { if (strcmp (exec->name, game_list.name) || strcmp (exec->version, game_list.version)) { dialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("The gamelist is from:\n" "%s %s\n" "and the current executable is:\n" "%s %s\n" "Do you want to rebuild the gamelist?"), game_list.name, game_list.version, exec->name, exec->version); } } if (dialog) { result = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); switch (result) { case GTK_RESPONSE_YES: gtk_widget_set_sensitive (main_gui.scrolled_window_games, FALSE); UPDATE_GUI; save_games_ini (); if (gamelist_parse (exec)) { gamelist_save (); load_games_ini (); load_catver_ini (); quick_check (); create_filterslist_content (); create_gamelist_content (); } gtk_widget_set_sensitive (main_gui.scrolled_window_games, TRUE); break; } } }