/** loadCareers * * Initialize the Careers module. First read the save file, then create the interface. * * Return a pointer to itself, or NULL if something failed. **/ MODULE_DATA* loadCareers() { MODULE_DATA* data; uint16_t numberEntries = 0; FILE* scoresFile; data = malloc (sizeof (MODULE_DATA)); if (data != NULL) { /** Open file and read it **/ scoresFile = fopen ("careers", "rb"); if (scoresFile != NULL) { fread (&numberEntries, sizeof (uint16_t), 1, scoresFile); /* Number of entries */ if (feof (scoresFile) == 0) { if ((numberEntries > 0) && (numberEntries <= 5)) { data->customData = malloc (sizeof (CAREER_DATA)); if (data->customData != NULL) { ((CAREER_DATA*) data->customData)->numEntries = numberEntries; ((CAREER_DATA*) data->customData)->careersPtr = malloc (sizeof (ENTRY_CAREER) * numberEntries); if (((CAREER_DATA*) data->customData)->careersPtr != NULL) { fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile); if (ferror (scoresFile) != 0) { fclose (scoresFile); free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); data->customData = NULL; numberEntries = 0; fprintf (stderr, fileCareersInvalid); } fclose (scoresFile); } else /* memory error */ { fclose (scoresFile); free (data->customData); free (data); error = EXIT_FAILURE; fprintf (stderr, outOfMemory); return NULL; } } else /* memory error */ { fclose (scoresFile); free (data); error = EXIT_FAILURE; fprintf (stderr, outOfMemory); return NULL; } } else /* invalid number of careers */ { data->customData = NULL; fprintf (stderr, fileCareersInvalid); fclose (scoresFile); numberEntries = 0; } } else /* eof encountered at opening, file doesn't exist */ { data->customData = NULL; fclose (scoresFile); numberEntries = 0; } } else /* error when opening 'careers' */ { data->customData = NULL; fprintf (stderr, couldntOpenCareers); numberEntries = 0; } /** Create interface **/ data->numIcons = 6; data->iconList = malloc (data->numIcons * sizeof(ICON)); if (data->iconList == NULL) { if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } free (data); fprintf (stderr, outOfMemory); error = EXIT_FAILURE; return NULL; } if (newIcon (&data->iconList[0], 521, 413, ICON_ENABLED, comeBack, "spr/icon_valid.png", NULL) == 0) { if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } free (data); return NULL; } if (newIcon (&data->iconList[1], 78, 237, (numberEntries > 0 ? ICON_ENABLED : ICON_DISABLED), career1, "spr/icon_book.png", "spr/icon_book_d.png") == 0) { deleteNIcons (data->iconList, 1); if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } free (data); return NULL; } if (newIcon (&data->iconList[2], 78, 271, (numberEntries > 1 ? ICON_ENABLED : ICON_DISABLED), career2, "spr/icon_book.png", "spr/icon_book_d.png") == 0) { deleteNIcons (data->iconList, 2); if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } free (data); return NULL; } if (newIcon (&data->iconList[3], 78, 305, (numberEntries > 2 ? ICON_ENABLED : ICON_DISABLED), career3, "spr/icon_book.png", "spr/icon_book_d.png") == 0) { deleteNIcons (data->iconList, 3); if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } free (data); return NULL; } if (newIcon (&data->iconList[4], 78, 339, (numberEntries > 3 ? ICON_ENABLED : ICON_DISABLED), career4, "spr/icon_book.png", "spr/icon_book_d.png") == 0) { deleteNIcons (data->iconList, 4); if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } free (data); return NULL; } if (newIcon (&data->iconList[5], 78, 373, (numberEntries > 4 ? ICON_ENABLED : ICON_DISABLED), career5, "spr/icon_book.png", "spr/icon_book_d.png") == 0) { deleteNIcons (data->iconList, 5); if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } free (data); return NULL; } data->background = loadSprite ("spr/bg_careers.png"); if (data->background == NULL) { if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } deleteNIcons (data->iconList, 6); free (data); fprintf (stderr, "%s\n", SDL_GetError()); error = EXIT_FAILURE; return NULL; } data->bgPosition.x = 0; data->bgPosition.y = 0; } else { fprintf (stderr, outOfMemory); error = EXIT_FAILURE; } return data; }
data = malloc(...);
if (!data) {
return NULL;
}
scoresFile = fopen (...);
if (!scoresFile) {
free(data);
return NULL;
}
fread(...);
if (feof (scoresFile)) {
fclose(scoreFile);
free(data);
return NULL;
}
...
Thepro (./1533) :
Pour la première partie, tu pourrais ne pas imbriquer tous les tests :
Thepro (./1533) :
Ensuite, tu essayes peut-être de faire trop de choses dans une seule fonction. Tu ne pourrais pas séparer la lecture du fichier et la création du MODULE_DATA ?
PpHd (./1534) :
Linux autorise à allouer plus de mémoires virtuelles qu'il ne peut en allouer physiquement), tu peux avoir un pointeur valide, mais dès que tu essayes d'y écrire, boum
PpHd (./1534) :
Terminaison (pas forcément du tien d'ailleurs).
PpHd (./1534) :
Un autre raison pour est que si ton programme n'arrive pas à allouer les 44 octets nécessaires à tes structures, il ne pourra rien faire du tout de toute facon.
Thepro (./1535) :
Tiens, il manque des tests pour savoir si les fread ont réussi
Zerosquare (./1536) :
Pour les erreurs fatales (qui font que tu vas terminer ton programme), atexit() est une solution assez élégante.
Folco (./1537) :
ps -> ah oué, tu me conseilles de définir une fonction void* my_alloc(size) qui fait abort si elle a foiré
Folco (./1537) :
Mais je crois qu'un appel à abort zappe les atexit()
Folco (./1537) :Pourquoi feof ou ferror ? Tu peux tester si fread a lu le bon nombre d'éléments. La valeur retournée doit être la même que celle entrée en 3e paramètre si tout s'est bien passé
Oui, rajoutés depuis, bien vu t'as l'oeil(feof et ferror)
Folco (./1537) :
Je sais pas quelles sont les bonnes stratégies sur PC. Sur TI en tout cas, j'ai toujours fait proprement (même si PreOS aurait pu tout faire pour moi).
int fonctionChargement() {
FILE *res1 = fopen(...);
int *res2 = 0L;
if (!res1) goto error;
res2 = malloc(...);
if (!res2) goto error;
...
return 0;
error:
if (res1) fclose(res1);
if (res2) free(res2);
return 1;
}
MODULE_DATA* data; uint16_t numberEntries = 0; FILE* scoresFile; const char* errorStr; data = malloc (sizeof (MODULE_DATA)); /* Out of memory */ if (data == NULL) return NULL; scoresFile = fopen ("careers", "rb"); /* File doesn't exist, just skip */ if (scoresFile == NULL) goto NoEntry; errorStr = fileCareersInvalid; /* Invalid file format, don't use it */ if ((fread (&numberEntries, sizeof (uint16_t), 1, scoresFile) != sizeof (uint16_t)) || (numberEntries > 5)) goto CloseFile; errorStr = outOfMemory; /* Out of memory */ data->customData = malloc (sizeof (CAREER_DATA)) if (data->customData == NULL) goto CloseFile; ((CAREER_DATA*) data->customData)->numEntries; ((CAREER_DATA*) data->customData)->careersPtr = malloc (numberEntries * sizeof (ENTRY_CAREER)); errorStr = outOfMemory; /* Out of memory */ if (((CAREER_DATA*) data->customData)->careersPtr == NULL) goto FreeCustomData; errorStr = fileCareersInvalid; /* Invalid file format, don't use it */ if (fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile) == sizeof (ENTRY_CAREER)) goto Success free (((CAREER_DATA*) data->customData)->careersPtr); FreeCustomData: free (data->customData); CloseFile: fprintf (stderr, errorStr); fclose (scoresFile); NoEntry: data->customData = NULL; numberEntries = 0;
Zerosquare (./1536) :
Pour les erreurs fatales (qui font que tu vas terminer ton programme), atexit() est une solution assez élégante.
/** loadCareers * * Initialize the Careers module. First read the save file, then create the interface. * * Return a pointer to itself, or NULL if something failed. **/ MODULE_DATA* loadCareers() { MODULE_DATA* data; uint16_t numberEntries; FILE* scoresFile; const char* errorStr; data = malloc (sizeof (MODULE_DATA)); /* Out of memory */ if (data == NULL) return NULL; /** Open file and read it **/ scoresFile = fopen ("careers", "rb"); /* File doesn't exist, just skip */ if (scoresFile == NULL) goto NoEntry; errorStr = fileCareersInvalid; /* Invalid file format, don't use it */ if ((fread (&numberEntries, sizeof (uint16_t), 1, scoresFile) != sizeof (uint16_t)) || (numberEntries > 5)) goto CloseFile; errorStr = outOfMemory; /* Out of memory */ data->customData = malloc (sizeof (CAREER_DATA)); if (data->customData == NULL) goto CloseFile; ((CAREER_DATA*) data->customData)->numEntries = numberEntries; ((CAREER_DATA*) data->customData)->careersPtr = malloc (numberEntries * sizeof (ENTRY_CAREER)); errorStr = outOfMemory; /* Out of memory */ if (((CAREER_DATA*) data->customData)->careersPtr == NULL) goto FreeCustomData; errorStr = fileCareersInvalid; /* Invalid file format, don't use it */ if (fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile) == sizeof (ENTRY_CAREER)) goto Success; free (((CAREER_DATA*) data->customData)->careersPtr); FreeCustomData: free (data->customData); CloseFile: fprintf (stderr, errorStr); fclose (scoresFile); NoEntry: data->customData = NULL; numberEntries = 0; /** Create interface **/ Success: data->numIcons = 0; data->iconList = malloc (7 * sizeof(ICON)); if (data->iconList == NULL) { fprintf (stderr, outOfMemory); goto FreeAll; } if (newIcon (&data->iconList[0], 521, 413, ICON_ENABLED, comeBack, "spr/icon_valid.png", NULL) == 0) goto FreeAll; data->numIcons ++; if (newIcon (&data->iconList[1], 78, 237, (numberEntries > 0 ? ICON_ENABLED : ICON_DISABLED), career1, "spr/icon_book.png", "spr/icon_book_d.png") == 0) goto FreeAll; data->numIcons ++; if (newIcon (&data->iconList[2], 78, 271, (numberEntries > 1 ? ICON_ENABLED : ICON_DISABLED), career2, "spr/icon_book.png", "spr/icon_book_d.png") == 0) goto FreeAll; data->numIcons ++; if (newIcon (&data->iconList[3], 78, 305, (numberEntries > 2 ? ICON_ENABLED : ICON_DISABLED), career3, "spr/icon_book.png", "spr/icon_book_d.png") == 0) goto FreeAll; data->numIcons ++; if (newIcon (&data->iconList[4], 78, 339, (numberEntries > 3 ? ICON_ENABLED : ICON_DISABLED), career4, "spr/icon_book.png", "spr/icon_book_d.png") == 0) goto FreeAll; data->numIcons ++; if (newIcon (&data->iconList[5], 78, 373, (numberEntries > 4 ? ICON_ENABLED : ICON_DISABLED), career5, "spr/icon_book.png", "spr/icon_book_d.png") == 0) goto FreeAll; data->numIcons ++; data->background = loadSprite ("spr/bg_careers.png"); if (data->background == NULL) goto FreeAll; data->bgPosition.x = 0; data->bgPosition.y = 0; return data; FreeAll: if (data->customData != NULL) { free (((CAREER_DATA*) data->customData)->careersPtr); free (data->customData); } if (data->iconList != NULL) { deleteNIcons(data->iconList, data->numIcons); free (data->iconList); } free (data); error = EXIT_FAILURE; return NULL; }
Folco (./1545) :Ça ne va pas fonctionner, car fread renvoie le nombre d'éléments lus et non la taille de ce qui est lu... Il faut donc vérifier que 1 (3e paramètre) est renvoyé plutôt que sizeof(uint16_t)if ((fread (&numberEntries, sizeof (uint16_t), 1, scoresFile) != sizeof (uint16_t)) || (numberEntries > 5))
if (fread (((CAREER_DATA*) data->customData)->careersPtr, sizeof (ENTRY_CAREER), numberEntries, scoresFile) == sizeof (ENTRY_CAREER))
Ici, tu as oublié de multiplier ce que tu compares à la sortie par numberEntries... Mais ce n'est pas bon, comme précédemment, il faut seulement comparer avec numberEntries Folco (./1551) :Absolument pas. Lecture/écriture d'une structure sur disque en C -> conversion de chaque membre à la main de/vers une forme binaire indépendante de la plateforme (tu choisis little-endian ou big-endian, et tu t'y tiens). Dans d'autres langages, il y a des fonctions qui s'occupent de ça automatiquement (ça s'appelle la sérialisation), mais pas en C.
Même si ça marche par hasard chez moi, est-ce que c'est garanti pour toutes les implémentations ?
Zerosquare (./1552) :
Absolument pas. Lecture/écriture d'une structure sur disque en C -> conversion de chaque membre à la main de/vers une forme binaire indépendante de la plateforme (tu choisis little-endian ou big-endian, et tu t'y tiens).
Brunni (./1553) :
Et il aime ça en plus
Folco (./1555) :Tout à fait.
La solution que je vois : écrire octet par octet (j'ai un tableau de 20 chars et 17 uint16) dans mon fichier, donc en décomposant les words en bytes, pas le choix ? Découpage, écriture (fputc je crois, je vais regarder man), et pour lire le fichier fgetc + reconstruction des words ? A ce moment-là, je devrais être indépendant autant de la représentation mémoire de ma structure que de l'endianness, non ?
Brunni (./1556) :
Solution compliquée: une interface avec des putByte, putShort, etc. que tu implémenteras une fois pour chacun des OS.
BOOL write_int32(FILE* f, INT32 v)
{
UINT8 buffer[4];
buffer[0] = v & 0xFF;
buffer[1] = (v >> 8) & 0xFF;
buffer[2] = (v >> 16) & 0xFF;
buffer[3] = (v >> 32) & 0xFF;
return fwrite(buffer, 4 * sizeof(UINT8), 1, f) == 1;
}
Brunni (./1556) :
que tu implémenteras une fois pour chacun des OS
Brunni (./1556) :
Et décomposer ton écriture champ par champ (et il ne faut pas oublier de mettre à jour si tu modifies quelque chose)