Je pense avoir compris , le soucis est de lire des fichiers non formaté, le cas d'un fichier .7z
Je pense avoir compris , le soucis est de lire des fichiers non formaté, le cas d'un fichier .7z
Utilise file.read() vers un buffer d'1MO plutôt que tenter de lire tout ton fichier d'un coup (ce qui surcharge la RAM et ne sert à rien).
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Bonjour,
J'ai essayé fileX.read (bufferX, sizeX); avec le code ci dessous mais je n'ai pas l'impression de bien utiliser le read :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 char* m_NameFile2="C:\\Users\\X\\Desktop\\info2.7z"; streampos sizeX; char * bufferX = new char []; ifstream fileX (m_NameFile2, ios::in|ios::binary|ios::ate); if (fileX.is_open()) { // sizeX = fileX.tellg(); // bufferX = new char [sizeX]; // fileX.seekg (0, ios::beg); sizeX =1024; while(!fileX.eof()) { fileX.read (bufferX, sizeX); } fileX.close(); cout << "the entire file content is in memory"; //buffer<< memblock; // delete[] memblock; } else cout << "Unable to open file"; SHA512_CTX ctx; SHA512_Init( &ctx ); SHA512_Update( &ctx, bufferX, strlen(bufferX) ); SHA512_Final(digest, &ctx); char* bufInStr = const_cast<char*>(mdString.data()); for ( int i = 0 ; i < SHA512_DIGEST_LENGTH ; ++i ) { // FYI : don't use sprintf_s() because \0 can't be inside std::string const char tabCaractHexa[] = "0123456789abcdef"; int valueToConvert = digest[i]; int first = (valueToConvert >> 4) & 0xF; // part 'hight' of the byte int second = valueToConvert & 0xF; // lower part -> base unit 16 bufInStr[i*2] = tabCaractHexa[first]; // conversion 0 to 15 in '0' to 'F' bufInStr[i*2+1] = tabCaractHexa[second]; // conversion 0 to 15 in '0' to 'F' } bHashDone = true; fileHash << mdString; std::cout <<"SHA512 digest of " << m_NameFile.c_str() << " is "<< mdString << std::endl;
Ce qu'il faut, c'est un truc de ce genre:
Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 char const * nameFile2="C:\\Users\\X\\Desktop\\info2.7z"; const size_t bufferSize = 1024; vector<char> bufferX(bufferSize); //Autant laisser une classe faire le new et le delete pour nous unsigned char digest[SHA512_DIGEST_LENGTH]; SHA512_CTX ctx; SHA512_Init( &ctx ); { ifstream fileX (nameFile2, ios::in|ios::binary); if(!fileX.is_open()) { cout << "Unable to open file" << endl; return; } while(fileX.read(&bufferX[0], bufferSize)) { size_t nbRead = fileX.gcount(); SHA512_Update( &ctx, &bufferX[0], nbRead ); } if(!fileX.eof()) { cout << "Error reading file." << endl; } } SHA512_Final(digest, &ctx); bHashDone = true; mdString = DisplayDigest(m_nameFile, digest); fileHash << mdString; } string DisplayDigest(string nameFile, const unsigned char digest[SHA512_DIGEST_LENGTH]) { vector<char> szBuffer(SHA512_DIGEST_LENGTH*2 + 1); for ( int i = 0 ; i < SHA512_DIGEST_LENGTH ; ++i ) { const char tabCaractHexa[] = "0123456789abcdef"; int valueToConvert = digest[i]; int first = (valueToConvert >> 4) & 0xF; // part 'hight' of the byte int second = valueToConvert & 0xF; // lower part -> base unit 16 szBuffer[i*2] = tabCaractHexa[first]; // conversion 0 to 15 in '0' to 'F' szBuffer[i*2+1] = tabCaractHexa[second]; // conversion 0 to 15 in '0' to 'F' } szBuffer[SHA512_DIGEST_LENGTH*2] = '\0'; string mdString(&szBuffer[0]); cout << "SHA512 digest of " << nameFile << " is " << mdString << endl; return mdString; }
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Merci pour tes conseils, j'ai essayé avec le code ci dessous ça compile et s’exécute sans erreur mais ne fonctionne pas, quand j’exécute le code en pas à pas je remarque que je ne rentre pas dans le while. Donc j'ai bien peur que ce while ne doit pas bien fonctionner.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 while(fileX.read(&bufferX[0], bufferSize)) { size_t nbRead = fileX.gcount(); SHA512_Update( &ctx, &bufferX[0], nbRead ); }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 char const * nameFile2="C:\\Users\\x\\Desktop\\info2.7z"; const size_t bufferSize = 1024; vector<char> bufferX(bufferSize); //Autant laisser une classe faire le new et le delete pour nous unsigned char digest[SHA512_DIGEST_LENGTH]; SHA512_CTX ctx; SHA512_Init( &ctx ); { ifstream fileX (nameFile2, ios::in|ios::binary); if(!fileX.is_open()) { cout << "Unable to open file" << endl; //return false; bHashDone = false; } while(fileX.read(&bufferX[0], bufferSize)) { size_t nbRead = fileX.gcount(); SHA512_Update( &ctx, &bufferX[0], nbRead ); } if(!fileX.eof()) { cout << "Error reading file." << endl; } } SHA512_Final(digest, &ctx); bHashDone = true; // mdString = DisplayDigest(nameFile2, digest); // fileHash << mdString; vector<char> szBuffer(SHA512_DIGEST_LENGTH*2 + 1); for ( int i = 0 ; i < SHA512_DIGEST_LENGTH ; ++i ) { const char tabCaractHexa[] = "0123456789abcdef"; int valueToConvert = digest[i]; int first = (valueToConvert >> 4) & 0xF; // part 'hight' of the byte int second = valueToConvert & 0xF; // lower part -> base unit 16 szBuffer[i*2] = tabCaractHexa[first]; // conversion 0 to 15 in '0' to 'F' szBuffer[i*2+1] = tabCaractHexa[second]; // conversion 0 to 15 in '0' to 'F' } szBuffer[SHA512_DIGEST_LENGTH*2] = '\0'; std::string mdString(&szBuffer[0]); cout << "SHA512 digest of " << nameFile2 << " is " << mdString << endl; fileHash << mdString;
Bonjour ,
Bonne nouvelle et mauvaise nouvelle .
La bonne nouvelle est qu'avec le code cidessous j'arrive à effectuer le hash d'un fichier .7z qui contient 1 fichier .txt d'une petite taille 20 kilo . Je verifie le hash avec l'outil File hash calculator. C'est bon Super.
La mauvaise nouvelle est que je ne calcule pas le bon hash pour un fichier de zip qui contient 2 fichiers .txt de 20kilo. Ou un zip qui contient juste un fichier .txt de 1.8go. Ou un zip qui contient deux zip d'un fichier .txt de 20ko.
Dans la mauvaise nouvelle on peut ajouter que les hash que j’obtiens sont tous différents.
Donc la fonction de hash fonctionne bien mais je pense que la lecture du fichier .7z dysfonctionne pour les gros fichier .7z. Svp , avez vous une idée de pourquoi je ne fais qu'une loop dans le while ci dessous pour un petit fichier et meme pour un gros fichier?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 while(!file.eof()) { char buff[bufferSize]; memset(buff,0x00,sizeof(buff)); file.read(buff,bufferSize); size_t nbRead = file.gcount(); SHA512_Update( &ctx, &buff[0], nbRead ); }
Ci dessous , le code source de toute ma fonction ,
Merci à vous .
Mes includes :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 #include <afx.h> #include <atlstr.h> #include <LibOpenSSL.h> #include <stdio.h> #include <string.h> #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <iostream> using namespace std;
Ma fonction :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 bool HashFile::generateHashInFile() { bool bHashDone = false; std::ofstream fileHash; std::ifstream file; for(int i=0;i<6;i++) { if(i==0){ m_NameFileHash="D:\\TODELETE\\0.sha512"; m_NameFile="D:\\TODELETE\\0.7z"; } if(i==1){ m_NameFileHash="D:\\TODELETE\\1.sha512"; m_NameFile="D:\\TODELETE\\1.7z"; } if(i==2){ m_NameFileHash="D:\\TODELETE\\2.sha512"; m_NameFile="D:\\TODELETE\\2.7z"; } if(i==3){ m_NameFileHash="D:\\TODELETE\\3.sha512"; m_NameFile="D:\\TODELETE\\3.7z"; } if(i==4){ m_NameFileHash="D:\\TODELETE\\4.sha512"; m_NameFile="D:\\TODELETE\\4.7z"; } if(i==5){ m_NameFileHash="D:\\TODELETE\\5.sha512"; m_NameFile="D:\\TODELETE\\5.7z"; } fileHash.clear(); file.clear(); SystemConfiguration::COutilsCommun::OpenOStream(fileHash,m_NameFileHash.c_str()); SystemConfiguration::COutilsCommun::OpenIStream(file, m_NameFile.c_str()); //cette ligne sert à ouvrir un stream SystemConfiguration::COutilsCommun::OpenOStream(fileHash,m_NameFileHash.c_str()); // if std::ifstream file is huge ( I have one error ) if(!file) { std::cout << "impossible d'ourvrir le fichier : " << m_NameFile.c_str() <<std::endl; std::cout << "taille du fichier : " << file.tellg() <<std::endl; } if(!fileHash) { std::cout << "impossible de creer le fichier : " << m_NameFileHash.c_str() <<std::endl; } if( (file)&& (fileHash) ) { unsigned char digest[SHA512_DIGEST_LENGTH]; std::string mdString(SHA512_DIGEST_LENGTH*2,' '); std::stringstream buffer; //// variable with the content of the file // buffer << file.rdbuf(); const size_t bufferSize = 1024; //vector<char> bufferX(bufferSize); //char buff[bufferSize]; char * finalBuff = NULL; SHA512_CTX ctx; SHA512_Init( &ctx ); if(!file.is_open()) { cout << "Unable to open file" << endl; bHashDone = false; } file.seekg(0,std::ios::beg); while(!file.eof()) { char buff[bufferSize]; memset(buff,0x00,sizeof(buff)); file.read(buff,bufferSize); size_t nbRead = file.gcount(); SHA512_Update( &ctx, &buff[0], nbRead ); } SHA512_Final(digest, &ctx); bHashDone = true; vector<char> szBuffer(SHA512_DIGEST_LENGTH*2 + 1); for ( int i = 0 ; i < SHA512_DIGEST_LENGTH ; ++i ) { const char tabCaractHexa[] = "0123456789abcdef"; int valueToConvert = digest[i]; int first = (valueToConvert >> 4) & 0xF; // part 'hight' of the byte int second = valueToConvert & 0xF; // lower part -> base unit 16 szBuffer[i*2] = tabCaractHexa[first]; // conversion 0 to 15 in '0' to 'F' szBuffer[i*2+1] = tabCaractHexa[second]; // conversion 0 to 15 in '0' to 'F' } szBuffer[SHA512_DIGEST_LENGTH*2] = '\0'; std::string mdString2(&szBuffer[0]); cout << "SHA512 digest of " << m_NameFile << " is " << mdString2 << endl; fileHash << mdString2; //clean the content of the variables buffer.clear(); mdString.clear(); mdString2.clear(); m_NameFile.clear(); m_NameFileHash.clear(); } //end if( (file)&& (fileHash) ) if( file.is_open() ) { SystemConfiguration::COutilsCommun::CloseIStream(file,m_NameFile.c_str()); } if( fileHash.is_open() ) { SystemConfiguration::COutilsCommun::CloseOStream(fileHash,m_NameFileHash.c_str()); } fileHash.close(); file.close(); }//le for pour test return bHashDone; } catch ( std::exception e) { std::cout << "impossible d'ourvrir le fichier : " << m_NameFile.c_str() <<std::endl; if( file.is_open() ) { SystemConfiguration::COutilsCommun::CloseIStream(file,m_NameFile.c_str()); } if( fileHash.is_open() ) { SystemConfiguration::COutilsCommun::CloseOStream(fileHash,m_NameFileHash.c_str()); } } return bHashDone; } else { std::cout << "\n\n Merci de specifier un nom de fichier à hasher et un nom de fichier qui contient le hash \n\n" <<std::endl; return bHashDone; } }
Peut-on voir le code de OpenIStream?
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Bonjour,
MErci pour ton aide et tes conseils , je pense que le probleme viens d'ici : while(!file.eof()) , faire un .eof n'est pas bon pour un stream , qu'est ce que je peux utiliser à la place de eof ?
LE code de OpenIStream est :
Code : Sélectionner tout - Visualiser dans une fenêtre à part static bool OpenIStream(std::ifstream& fichier, const char* fileStream, std::ios_base::open_mode mode=std::ios_base::in);
Bonjour Médinoc,
Bonne nouvelle , j'arrive enfin à générer le bon hash de 5 archives sur 7.
Je me suis dis que j'ouvrai mal le fichier de zip, donc j'ai remplacé
ceci /* SystemConfiguration::COutilsCommun::OpenIStream(file, m_NameFile.c_str());*/
par ceci : file.open(m_NameFile.c_str(),std::ios::binary);
ma boucle while deviens :
En detail Le hash correspond pour presque tout mes archives :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 while(!(file>>std::ws).eof()) // while((file>>std::ws)&&!file.eof()&&file>>c) char buff[bufferSize]; memset(buff,0x00,sizeof(buff)); int buffer2; while(!(file>>std::ws).eof()) { file.read(buff,sizeof(buff)); size_t nbRead = file.gcount(); SHA512_Update( &ctx, &buff[0], nbRead ); // if(file.fail()) // { // //break; // } //memset(buff,0x00,sizeof(buff)); } if(file.eof()) { std::cout<<"lecture fichier ok\n"; }else { std::cout<<"erreur lecture fichier ok\n"; }
Ok pour: Un fichier de .7z qui contient Un fichier texte de 4ko
Non ok pour : un fichier .7z qui contient un dossier qui contient un fichier texte de 17ko et un autre de 1,9Go dans ce dossier
Ok pour : un fichier de .7z qui contient 2 fichiers .7z (un .7z contient un fichier texte de 1.9Go et l'autre 7z contient un fichier texte de 17ko)
Ok pour : Un fichier .7z qui contient 1 fichier texte de 0ko et 1 fichier 7z (qui contient un 1 fichier texte de 4ko)
Ok pour : Un fichier . 7z qui contient 2 fichier de .7z ( le premier .7z contient 2 fichier texte de 50k et 70k, le 2eme .7z contient juste une fichier texte de 1.9Go)
Non ok pour : mon archive qui n'est pas une archive de test (Elle contient une archive avec des dossiers et une multitude de fichier de différent )
ci dessous mon code
Encore une fois merci pour tes conseils
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177 bool HashFile::generateHashInFile() { bool bHashDone = false; std::ofstream fileHash; std::ifstream file; if( NULL != m_NameFile.c_str() ) { try { for(int i=0;i<5;i++) { // if(i==0){ // m_NameFileHash="D:\\TODELETE\\0.sha512"; // m_NameFile="D:\\TODELETE\\0.7z"; // } // if(i==1){ // m_NameFileHash="D:\\TODELETE\\1.sha512"; // m_NameFile="D:\\TODELETE\\1.7z"; // } // if(i==2){ // m_NameFileHash="D:\\TODELETE\\2.sha512"; // m_NameFile="D:\\TODELETE\\2.7z"; // } // if(i==3){ // m_NameFileHash="D:\\TODELETE\\3.sha512"; // m_NameFile="D:\\TODELETE\\3.7z"; // } // if(i==4){ // m_NameFileHash="D:\\TODELETE\\4.sha512"; // m_NameFile="D:\\TODELETE\\4.7z"; // } // if(i==5){ // m_NameFileHash="D:\\TODELETE\\5.sha512"; // m_NameFile="D:\\TODELETE\\5.7z"; // } fileHash.clear(); file.clear(); SystemConfiguration::COutilsCommun::OpenOStream(fileHash,m_NameFileHash.c_str()); /* SystemConfiguration::COutilsCommun::OpenIStream(file, m_NameFile.c_str());*/ file.open(m_NameFile.c_str(),std::ios::binary); // if std::ifstream file is huge ( I have one error ) if(!file) { std::cout << "impossible d'ourvrir le fichier : " << m_NameFile.c_str() <<std::endl; std::cout << "taille du fichier : " << file.tellg() <<std::endl; } if(!fileHash) { std::cout << "impossible de creer le fichier : " << m_NameFileHash.c_str() <<std::endl; } if( (file)&& (fileHash) ) { unsigned char digest[SHA512_DIGEST_LENGTH]; std::string mdString(SHA512_DIGEST_LENGTH*2,' '); std::stringstream buffer; //// variable with the content of the file // buffer << file.rdbuf(); const size_t bufferSize = 1024; //vector<char> bufferX(bufferSize); //char buff[bufferSize]; char * finalBuff = NULL; SHA512_CTX ctx; SHA512_Init( &ctx ); if(!file.is_open()) { cout << "Unable to open file" << endl; bHashDone = false; } file.seekg(0,std::ios::beg); // FYI : while(!file.eof()) don't work because this check the end of bit only in the stream state // char c; // while(!(file>>std::ws).eof()) // while((file>>std::ws)&&!file.eof()&&file>>c) char buff[bufferSize]; memset(buff,0x00,sizeof(buff)); int buffer2; while(!(file>>std::ws).eof()) { file.read(buff,sizeof(buff)); size_t nbRead = file.gcount(); SHA512_Update( &ctx, &buff[0], nbRead ); // if(file.fail()) // { // //break; // } //memset(buff,0x00,sizeof(buff)); } if(file.eof()) { std::cout<<"lecture fichier ok\n"; }else { std::cout<<"erreur lecture fichier ok\n"; } SHA512_Final(digest, &ctx); bHashDone = true; vector<char> szBuffer(SHA512_DIGEST_LENGTH*2 + 1); for ( int i = 0 ; i < SHA512_DIGEST_LENGTH ; ++i ) { const char tabCaractHexa[] = "0123456789abcdef"; int valueToConvert = digest[i]; int first = (valueToConvert >> 4) & 0xF; // part 'hight' of the byte int second = valueToConvert & 0xF; // lower part -> base unit 16 szBuffer[i*2] = tabCaractHexa[first]; // conversion 0 to 15 in '0' to 'F' szBuffer[i*2+1] = tabCaractHexa[second]; // conversion 0 to 15 in '0' to 'F' } szBuffer[SHA512_DIGEST_LENGTH*2] = '\0'; std::string mdString2(&szBuffer[0]); cout << "SHA512 digest of " << m_NameFile << " is " << mdString2 << endl; fileHash << mdString2; //clean the content of the variables buffer.clear(); mdString.clear(); mdString2.clear(); m_NameFile.clear(); m_NameFileHash.clear(); } //end if( (file)&& (fileHash) ) if( file.is_open() ) { SystemConfiguration::COutilsCommun::CloseIStream(file,m_NameFile.c_str()); } if( fileHash.is_open() ) { SystemConfiguration::COutilsCommun::CloseOStream(fileHash,m_NameFileHash.c_str()); } fileHash.close(); file.close(); }//le for pour test return bHashDone; } catch ( std::exception e) { std::cout << "impossible d'ourvrir le fichier : " << m_NameFile.c_str() <<std::endl; if( file.is_open() ) { SystemConfiguration::COutilsCommun::CloseIStream(file,m_NameFile.c_str()); } if( fileHash.is_open() ) { SystemConfiguration::COutilsCommun::CloseOStream(fileHash,m_NameFileHash.c_str()); } } return bHashDone; } else { std::cout << "\n\n Merci de specifier un nom de fichier à hasher et un nom de fichier qui contient le hash \n\n" <<std::endl; return bHashDone; } }
Hélas, là je suis à court d'idées. Je ne connais pas des masses les flux C++, et je n'ai pas de OpenSSL pour tester ton code.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager