]>
Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* s_fileio.c |
2 | * | |
3 | * Micropolis, Unix Version. This game was released for the Unix platform | |
4 | * in or about 1990 and has been modified for inclusion in the One Laptop | |
5 | * Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If | |
6 | * you need assistance with this program, you may contact: | |
7 | * http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org. | |
8 | * | |
9 | * This program is free software: you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation, either version 3 of the License, or (at | |
12 | * your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. You should have received a | |
18 | * copy of the GNU General Public License along with this program. If | |
19 | * not, see <http://www.gnu.org/licenses/>. | |
20 | * | |
21 | * ADDITIONAL TERMS per GNU GPL Section 7 | |
22 | * | |
23 | * No trademark or publicity rights are granted. This license does NOT | |
24 | * give you any right, title or interest in the trademark SimCity or any | |
25 | * other Electronic Arts trademark. You may not distribute any | |
26 | * modification of this program using the trademark SimCity or claim any | |
27 | * affliation or association with Electronic Arts Inc. or its employees. | |
28 | * | |
29 | * Any propagation or conveyance of this program must include this | |
30 | * copyright notice and these terms. | |
31 | * | |
32 | * If you convey this program (or any modifications of it) and assume | |
33 | * contractual liability for the program to recipients of it, you agree | |
34 | * to indemnify Electronic Arts for any liability that those contractual | |
35 | * assumptions impose on Electronic Arts. | |
36 | * | |
37 | * You may not misrepresent the origins of this program; modified | |
38 | * versions of the program must be marked as such and not identified as | |
39 | * the original program. | |
40 | * | |
41 | * This disclaimer supplements the one included in the General Public | |
42 | * License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS | |
43 | * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY | |
44 | * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF | |
45 | * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS | |
46 | * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES, | |
47 | * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY, | |
48 | * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY | |
49 | * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING, | |
50 | * USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST | |
51 | * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL | |
52 | * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE | |
53 | * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE | |
54 | * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE | |
55 | * CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR | |
56 | * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME | |
57 | * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED | |
58 | * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A | |
59 | * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY | |
60 | * NOT APPLY TO YOU. | |
61 | */ | |
62 | #include "sim.h" | |
63 | ||
64 | ||
6a5fa4e0 MG |
65 | #define SWAP_SHORTS(a,b) _swap_shorts(a,b) |
66 | #define SWAP_LONGS(a,b) _swap_longs(a,b) | |
67 | #define HALF_SWAP_LONGS(a,b) _half_swap_longs(a,b) | |
68 | ||
e9c442bb MG |
69 | #define NOOP_ON_BE { int test = 1; if (!(*(unsigned char*) (&test))) return; } |
70 | ||
6a5fa4e0 MG |
71 | static void |
72 | _swap_shorts(short *buf, int len) | |
73 | { | |
74 | int i; | |
75 | ||
e9c442bb MG |
76 | NOOP_ON_BE; |
77 | ||
6a5fa4e0 MG |
78 | /* Flip bytes in each short! */ |
79 | for (i = 0; i < len; i++) { | |
80 | *buf = ((*buf & 0xFF) <<8) | ((*buf &0xFF00) >>8); | |
81 | buf++; | |
82 | } | |
83 | } | |
84 | ||
85 | static void | |
86 | _swap_longs(long *buf, int len) | |
87 | { | |
88 | int i; | |
89 | ||
e9c442bb MG |
90 | NOOP_ON_BE; |
91 | ||
6a5fa4e0 MG |
92 | /* Flip bytes in each long! */ |
93 | for (i = 0; i < len; i++) { | |
94 | long l = *buf; | |
95 | *buf = | |
96 | ((l & 0x000000ff) << 24) | | |
97 | ((l & 0x0000ff00) << 8) | | |
98 | ((l & 0x00ff0000) >> 8) | | |
99 | ((l & 0xff000000) >> 24); | |
100 | buf++; | |
101 | } | |
102 | } | |
103 | ||
104 | static void | |
105 | _half_swap_longs(long *buf, int len) | |
106 | { | |
107 | int i; | |
108 | ||
e9c442bb MG |
109 | NOOP_ON_BE |
110 | ||
6a5fa4e0 MG |
111 | /* Flip bytes in each long! */ |
112 | for (i = 0; i < len; i++) { | |
113 | long l = *buf; | |
114 | *buf = | |
115 | ((l & 0x0000ffff) << 16) | | |
116 | ((l & 0xffff0000) >> 16); | |
117 | buf++; | |
118 | } | |
119 | } | |
120 | ||
6a5fa4e0 MG |
121 | static int |
122 | _load_short(short *buf, int len, FILE *f) | |
123 | { | |
124 | if (fread(buf, sizeof(short), len, f) != len) | |
125 | return 0; | |
126 | ||
127 | SWAP_SHORTS(buf, len); /* to intel */ | |
128 | ||
129 | return 1; | |
130 | } | |
131 | ||
132 | ||
133 | static int | |
134 | _load_long(long *buf, int len, FILE *f) | |
135 | { | |
136 | if (fread(buf, sizeof(long), len, f) != len) | |
137 | return 0; | |
138 | ||
139 | SWAP_LONGS(buf, len); /* to intel */ | |
140 | ||
141 | return 1; | |
142 | } | |
143 | ||
144 | ||
145 | static int | |
146 | _save_short(short *buf, int len, FILE *f) | |
147 | { | |
148 | ||
149 | SWAP_SHORTS(buf, len); /* to MAC */ | |
150 | ||
151 | if (fwrite(buf, sizeof(short), len, f) != len) | |
152 | return 0; | |
153 | ||
154 | SWAP_SHORTS(buf, len); /* back to intel */ | |
155 | ||
156 | return 1; | |
157 | } | |
158 | ||
159 | ||
160 | static int | |
161 | _save_long(long *buf, int len, FILE *f) | |
162 | { | |
163 | ||
164 | SWAP_LONGS(buf, len); /* to MAC */ | |
165 | ||
166 | if (fwrite(buf, sizeof(long), len, f) != len) | |
167 | return 0; | |
168 | ||
169 | SWAP_LONGS(buf, len); /* back to intel */ | |
170 | ||
171 | return 1; | |
172 | } | |
173 | ||
174 | ||
175 | static | |
176 | int | |
177 | _load_file(char *filename, char *dir) | |
178 | { | |
179 | FILE *f; | |
180 | char path[512]; | |
181 | QUAD size; | |
182 | ||
183 | #ifdef MSDOS | |
184 | if (dir != NULL) { | |
185 | sprintf(path, "%s\\%s", dir, filename); | |
186 | filename = path; | |
187 | } | |
188 | if ((f = fopen(filename, "rb")) == NULL) { | |
189 | return 0; | |
190 | } | |
191 | #else | |
192 | if (dir != NULL) { | |
193 | sprintf(path, "%s/%s", dir, filename); | |
194 | filename = path; | |
195 | } | |
196 | if ((f = fopen(filename, "r")) == NULL) { | |
197 | return (0); | |
198 | } | |
199 | #endif | |
200 | ||
201 | fseek(f, 0L, SEEK_END); | |
202 | size = ftell(f); | |
203 | fseek(f, 0L, SEEK_SET); | |
204 | ||
205 | switch (size) { | |
206 | case 27120: /* Normal city */ | |
207 | break; | |
208 | ||
209 | case 99120: /* 2x2 city */ | |
210 | break; | |
211 | ||
212 | case 219120: /* 3x3 city */ | |
213 | break; | |
214 | ||
215 | default: | |
216 | return (0); | |
217 | } | |
218 | ||
219 | if ((_load_short(ResHis, HISTLEN / 2, f) == 0) || | |
220 | (_load_short(ComHis, HISTLEN / 2, f) == 0) || | |
221 | (_load_short(IndHis, HISTLEN / 2, f) == 0) || | |
222 | (_load_short(CrimeHis, HISTLEN / 2, f) == 0) || | |
223 | (_load_short(PollutionHis, HISTLEN / 2, f) == 0) || | |
224 | (_load_short(MoneyHis, HISTLEN / 2, f) == 0) || | |
225 | (_load_short(MiscHis, MISCHISTLEN / 2, f) == 0) || | |
226 | (_load_short((&Map[0][0]), WORLD_X * WORLD_Y, f) < 0)) { | |
227 | ||
228 | /* TODO: report error */ | |
229 | fclose(f); | |
230 | return(0); | |
231 | } | |
232 | ||
233 | fclose(f); | |
234 | return(1); | |
235 | } | |
236 | ||
237 | ||
238 | int loadFile(char *filename) | |
239 | { | |
240 | long l; | |
241 | ||
242 | if (_load_file(filename, NULL) == 0) | |
243 | return(0); | |
244 | ||
245 | /* total funds is a long..... MiscHis is array of shorts */ | |
246 | /* total funds is being put in the 50th & 51th word of MiscHis */ | |
247 | /* find the address, cast the ptr to a lontPtr, take contents */ | |
248 | ||
249 | l = *(QUAD *)(MiscHis + 50); | |
250 | HALF_SWAP_LONGS(&l, 1); | |
251 | SetFunds(l); | |
252 | ||
253 | l = *(QUAD *)(MiscHis + 8); | |
254 | HALF_SWAP_LONGS(&l, 1); | |
255 | CityTime = l; | |
256 | ||
257 | autoBulldoze = MiscHis[52]; /* flag for autoBulldoze */ | |
258 | autoBudget = MiscHis[53]; /* flag for autoBudget */ | |
259 | autoGo = MiscHis[54]; /* flag for autoGo */ | |
260 | UserSoundOn = MiscHis[55]; /* flag for the sound on/off */ | |
261 | CityTax = MiscHis[56]; | |
262 | SimSpeed = MiscHis[57]; | |
263 | // sim_skips = sim_skip = 0; | |
264 | ChangeCensus(); | |
265 | MustUpdateOptions = 1; | |
266 | ||
267 | /* yayaya */ | |
268 | ||
269 | l = *(QUAD *)(MiscHis + 58); | |
270 | HALF_SWAP_LONGS(&l, 1); | |
271 | policePercent = l / 65536.0; | |
272 | ||
273 | l = *(QUAD *)(MiscHis + 60); | |
274 | HALF_SWAP_LONGS(&l, 1); | |
275 | firePercent = l / 65536.0; | |
276 | ||
277 | l = *(QUAD *)(MiscHis + 62); | |
278 | HALF_SWAP_LONGS(&l, 1); | |
279 | roadPercent = l / 65536.0; | |
280 | ||
281 | policePercent = (*(QUAD*)(MiscHis + 58)) / 65536.0; /* and 59 */ | |
282 | firePercent = (*(QUAD*)(MiscHis + 60)) / 65536.0; /* and 61 */ | |
283 | roadPercent =(*(QUAD*)(MiscHis + 62)) / 65536.0; /* and 63 */ | |
284 | ||
285 | if (CityTime < 0) | |
286 | CityTime = 0; | |
287 | if ((CityTax > 20) || (CityTax < 0)) | |
288 | CityTax = 7; | |
289 | if ((SimSpeed < 0) || (SimSpeed > 3)) | |
290 | SimSpeed = 3; | |
291 | ||
292 | setSpeed(SimSpeed); | |
293 | setSkips(0); | |
294 | ||
295 | InitFundingLevel(); | |
296 | ||
297 | /* set the scenario id to 0 */ | |
298 | InitWillStuff(); | |
299 | ScenarioID = 0; | |
300 | InitSimLoad = 1; | |
301 | DoInitialEval = 0; | |
302 | DoSimInit(); | |
303 | InvalidateEditors(); | |
304 | InvalidateMaps(); | |
305 | ||
306 | return (1); | |
307 | } | |
308 | ||
309 | ||
310 | int saveFile(char *filename) | |
311 | { | |
312 | long l; | |
313 | FILE *f; | |
314 | ||
315 | #ifdef MSDOS | |
316 | if ((f = fopen(filename, "wb")) == NULL) { | |
317 | #else | |
318 | if ((f = fopen(filename, "w")) == NULL) { | |
319 | #endif | |
320 | /* TODO: report error */ | |
321 | return(0); | |
322 | } | |
323 | ||
324 | /* total funds is a long..... MiscHis is array of ints */ | |
325 | /* total funds is bien put in the 50th & 51th word of MiscHis */ | |
326 | /* find the address, cast the ptr to a lontPtr, take contents */ | |
327 | ||
328 | l = TotalFunds; | |
329 | HALF_SWAP_LONGS(&l, 1); | |
330 | (*(QUAD *)(MiscHis + 50)) = l; | |
331 | ||
332 | l = CityTime; | |
333 | HALF_SWAP_LONGS(&l, 1); | |
334 | (*(QUAD *)(MiscHis + 8)) = l; | |
335 | ||
336 | MiscHis[52] = autoBulldoze; /* flag for autoBulldoze */ | |
337 | MiscHis[53] = autoBudget; /* flag for autoBudget */ | |
338 | MiscHis[54] = autoGo; /* flag for autoGo */ | |
339 | MiscHis[55] = UserSoundOn; /* flag for the sound on/off */ | |
340 | MiscHis[57] = SimSpeed; | |
341 | MiscHis[56] = CityTax; /* post release */ | |
342 | ||
343 | /* yayaya */ | |
344 | ||
345 | l = (int)(policePercent * 65536); | |
346 | HALF_SWAP_LONGS(&l, 1); | |
347 | (*(QUAD *)(MiscHis + 58)) = l; | |
348 | ||
349 | l = (int)(firePercent * 65536); | |
350 | HALF_SWAP_LONGS(&l, 1); | |
351 | (*(QUAD *)(MiscHis + 60)) = l; | |
352 | ||
353 | l = (int)(roadPercent * 65536); | |
354 | HALF_SWAP_LONGS(&l, 1); | |
355 | (*(QUAD *)(MiscHis + 62)) = l; | |
356 | ||
357 | if ((_save_short(ResHis, HISTLEN / 2, f) == 0) || | |
358 | (_save_short(ComHis, HISTLEN / 2, f) == 0) || | |
359 | (_save_short(IndHis, HISTLEN / 2, f) == 0) || | |
360 | (_save_short(CrimeHis, HISTLEN / 2, f) == 0) || | |
361 | (_save_short(PollutionHis, HISTLEN / 2, f) == 0) || | |
362 | (_save_short(MoneyHis, HISTLEN / 2, f) == 0) || | |
363 | (_save_short(MiscHis, MISCHISTLEN / 2, f) == 0) || | |
364 | (_save_short((&Map[0][0]), WORLD_X * WORLD_Y, f) < 0)) { | |
365 | ||
366 | /* TODO: report error */ | |
367 | fclose(f); | |
368 | return(0); | |
369 | } | |
370 | ||
371 | fclose(f); | |
372 | return(1); | |
373 | } | |
374 | ||
375 | ||
376 | LoadScenario(short s) | |
377 | { | |
378 | char *name, *fname; | |
379 | ||
380 | if (CityFileName != NULL) { | |
381 | ckfree(CityFileName); | |
382 | CityFileName = NULL; | |
383 | } | |
384 | ||
385 | SetGameLevel(0); | |
386 | ||
387 | if ((s < 1) || (s > 8)) s = 1; | |
388 | ||
389 | switch (s) { | |
390 | case 1: | |
391 | name = "Dullsville"; | |
392 | fname = "snro.111"; | |
393 | ScenarioID = 1; | |
394 | CityTime = ((1900 - 1900) * 48) + 2; | |
395 | SetFunds(5000); | |
396 | break; | |
397 | case 2: | |
398 | name = "San Francisco"; | |
399 | fname = "snro.222"; | |
400 | ScenarioID = 2; | |
401 | CityTime = ((1906 - 1900) * 48) + 2; | |
402 | SetFunds(20000); | |
403 | break; | |
404 | case 3: | |
405 | name = "Hamburg"; | |
406 | fname = "snro.333"; | |
407 | ScenarioID = 3; | |
408 | CityTime = ((1944 - 1900) * 48) + 2; | |
409 | SetFunds(20000); | |
410 | break; | |
411 | case 4: | |
412 | name = "Bern"; | |
413 | fname = "snro.444"; | |
414 | ScenarioID = 4; | |
415 | CityTime = ((1965 - 1900) * 48) + 2; | |
416 | SetFunds(20000); | |
417 | break; | |
418 | case 5: | |
419 | name = "Tokyo"; | |
420 | fname = "snro.555"; | |
421 | ScenarioID = 5; | |
422 | CityTime = ((1957 - 1900) * 48) + 2; | |
423 | SetFunds(20000); | |
424 | break; | |
425 | case 6: | |
426 | name = "Detroit"; | |
427 | fname = "snro.666"; | |
428 | ScenarioID = 6; | |
429 | CityTime = ((1972 - 1900) * 48) + 2; | |
430 | SetFunds(20000); | |
431 | break; | |
432 | case 7: | |
433 | name = "Boston"; | |
434 | fname = "snro.777"; | |
435 | ScenarioID = 7; | |
436 | CityTime = ((2010 - 1900) * 48) + 2; | |
437 | SetFunds(20000); | |
438 | break; | |
439 | case 8: | |
440 | name = "Rio de Janeiro"; | |
441 | fname = "snro.888"; | |
442 | ScenarioID = 8; | |
443 | CityTime = ((2047 - 1900) * 48) + 2; | |
444 | SetFunds(20000); | |
445 | break; | |
446 | } | |
447 | ||
448 | setAnyCityName(name); | |
449 | // sim_skips = sim_skip = 0; | |
450 | InvalidateMaps(); | |
451 | InvalidateEditors(); | |
452 | setSpeed(3); | |
453 | CityTax = 7; | |
454 | gettimeofday(&start_time, NULL); | |
455 | ||
456 | _load_file(fname, ResourceDir); | |
457 | ||
458 | InitWillStuff(); | |
459 | InitFundingLevel(); | |
460 | UpdateFunds(); | |
461 | InvalidateEditors(); | |
462 | InvalidateMaps(); | |
463 | InitSimLoad = 1; | |
464 | DoInitialEval = 0; | |
465 | DoSimInit(); | |
466 | DidLoadScenario(); | |
467 | Kick(); | |
468 | } | |
469 | ||
470 | ||
471 | DidLoadScenario() | |
472 | { | |
473 | Eval("UIDidLoadScenario"); | |
474 | } | |
475 | ||
476 | ||
477 | int LoadCity(char *filename) | |
478 | { | |
479 | char *cp; | |
480 | char msg[256]; | |
481 | ||
482 | if (loadFile(filename)) { | |
483 | if (CityFileName != NULL) | |
484 | ckfree(CityFileName); | |
485 | CityFileName = (char *)ckalloc(strlen(filename) + 1); | |
486 | strcpy(CityFileName, filename); | |
487 | ||
488 | if (cp = (char *)rindex(filename, '.')) | |
489 | *cp = 0; | |
490 | #ifdef MSDOS | |
491 | if (cp = (char *)rindex(filename, '\\')) | |
492 | #else | |
493 | if (cp = (char *)rindex(filename, '/')) | |
494 | #endif | |
495 | cp++; | |
496 | else | |
497 | cp = filename; | |
498 | filename = (char *)ckalloc(strlen(cp) + 1); | |
499 | strcpy(filename, cp); | |
500 | setCityName(filename); | |
501 | gettimeofday(&start_time, NULL); | |
502 | ||
503 | InvalidateMaps(); | |
504 | InvalidateEditors(); | |
505 | DidLoadCity(); | |
506 | return (1); | |
507 | } else { | |
508 | sprintf(msg, "Unable to load a city from the file named \"%s\". %s", | |
509 | filename ? filename : "(null)", | |
510 | errno ? strerror(errno) : ""); | |
511 | DidntLoadCity(msg); | |
512 | return (0); | |
513 | } | |
514 | } | |
515 | ||
516 | ||
517 | DidLoadCity() | |
518 | { | |
519 | Eval("UIDidLoadCity"); | |
520 | } | |
521 | ||
522 | ||
523 | DidntLoadCity(char *msg) | |
524 | { | |
525 | char buf[1024]; | |
526 | sprintf(buf, "UIDidntLoadCity {%s}", msg); | |
527 | Eval(buf); | |
528 | } | |
529 | ||
530 | ||
531 | SaveCity() | |
532 | { | |
533 | char msg[256]; | |
534 | ||
535 | if (CityFileName == NULL) { | |
536 | DoSaveCityAs(); | |
537 | } else { | |
538 | if (saveFile(CityFileName)) | |
539 | DidSaveCity(); | |
540 | else { | |
541 | sprintf(msg, "Unable to save the city to the file named \"%s\". %s", | |
542 | CityFileName ? CityFileName : "(null)", | |
543 | errno ? strerror(errno) : ""); | |
544 | DidntSaveCity(msg); | |
545 | } | |
546 | } | |
547 | } | |
548 | ||
549 | ||
550 | DoSaveCityAs() | |
551 | { | |
552 | Eval("UISaveCityAs"); | |
553 | } | |
554 | ||
555 | ||
556 | DidSaveCity() | |
557 | { | |
558 | Eval("UIDidSaveCity"); | |
559 | } | |
560 | ||
561 | ||
562 | DidntSaveCity(char *msg) | |
563 | { | |
564 | char buf[1024]; | |
565 | sprintf(buf, "UIDidntSaveCity {%s}", msg); | |
566 | Eval(buf); | |
567 | } | |
568 | ||
569 | ||
570 | SaveCityAs(char *filename) | |
571 | { | |
572 | char msg[256]; | |
573 | char *cp; | |
574 | ||
575 | if (CityFileName != NULL) | |
576 | ckfree(CityFileName); | |
577 | CityFileName = (char *)ckalloc(strlen(filename) + 1); | |
578 | strcpy(CityFileName, filename); | |
579 | ||
580 | if (saveFile(CityFileName)) { | |
581 | if (cp = (char *)rindex(filename, '.')) | |
582 | *cp = 0; | |
583 | #ifdef MSDOS | |
584 | if (cp = (char *)rindex(filename, '\\')) | |
585 | #else | |
586 | if (cp = (char *)rindex(filename, '/')) | |
587 | #endif | |
588 | cp++; | |
589 | else | |
590 | cp = filename; | |
591 | filename = (char *)ckalloc(strlen(cp) + 1); | |
592 | strcpy(filename, cp); | |
593 | setCityName(cp); | |
594 | DidSaveCity(); | |
595 | } else { | |
596 | sprintf(msg, "Unable to save the city to the file named \"%s\". %s", | |
597 | CityFileName ? CityFileName : "(null)", | |
598 | errno ? strerror(errno) : ""); | |
599 | DidntSaveCity(msg); | |
600 | } | |
601 | } | |
602 | ||
603 |