Micropolis
fileio.cpp
Go to the documentation of this file.
1/* fileio.cpp
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
77
78
79#include "micropolis.h"
80
81
83
84
85#if !defined(__BIG_ENDIAN__)
86
92#define SWAP_SHORTS(buf, len) swap_shorts(buf, len)
93
99#define HALF_SWAP_LONGS(buf, len) half_swap_longs(buf, len)
100
106static void swap_shorts(short *buf, int len)
107{
108 int i;
109
110 /* Flip bytes in each short! */
111 for (i = 0; i < len; i++) {
112 *buf = ((*buf & 0xFF) <<8) | ((*buf &0xFF00) >>8);
113 buf++;
114 }
115}
116
117
123static void half_swap_longs(long *buf, int len)
124{
125 int i;
126
127 /* Flip bytes in each long! */
128 for (i = 0; i < len; i++) {
129 long l = *buf;
130 *buf =
131 ((l & 0x0000ffff) << 16) |
132 ((l & 0xffff0000) >> 16);
133 buf++;
134 }
135}
136
137
138#else
139
140
148#define SWAP_SHORTS(buf, len)
149
157#define HALF_SWAP_LONGS(buf, len)
158
159
160#endif
161
171static bool load_short(short *buf, int len, FILE *f)
172{
173 size_t result = fread(buf, sizeof(short), len, f);
174
175 if ((int)result != len) {
176 return false;
177 }
178
179 SWAP_SHORTS(buf, len); /* to intel */
180
181 return true;
182}
183
184
194static bool save_short(short *buf, int len, FILE *f)
195{
196 SWAP_SHORTS(buf, len); /* to MAC */
197
198 if ((int)fwrite(buf, sizeof(short), len, f) != len) {
199 return false;
200 }
201
202 SWAP_SHORTS(buf, len); /* back to intel */
203
204 return true;
205}
206
213bool Micropolis::loadFileData(const std::string &filename)
214{
215 FILE *f;
216 Quad size;
217
218 // Open the file.
219 f = fopen(filename.c_str(), "rb");
220
221 // open() failed; report failure.
222 if (f == NULL) {
223 return false;
224 }
225
226 fseek(f, 0L, SEEK_END);
227 size = ftell(f);
228 fseek(f, 0L, SEEK_SET);
229
230 int mapSizeShort = // 12000
232 int mapSize = // 24000
233 mapSizeShort * sizeof(short);
234 int historySize = // 3120
235 (6 * HISTORY_LENGTH) +
237 int mapFileSize = // 27120
238 historySize + mapSize;
239 int mopFileSize = // 51120
240 historySize + mapSize + mapSize;
241 bool hasMop =
242 size == mopFileSize;
243 bool isValid =
244 hasMop || (size == mapFileSize);
245
246 bool result =
247 isValid &&
248 load_short(resHist, HISTORY_LENGTH / sizeof(short), f) &&
249 load_short(comHist, HISTORY_LENGTH / sizeof(short), f) &&
250 load_short(indHist, HISTORY_LENGTH / sizeof(short), f) &&
251 load_short(crimeHist, HISTORY_LENGTH / sizeof(short), f) &&
252 load_short(pollutionHist, HISTORY_LENGTH / sizeof(short), f) &&
253 load_short(moneyHist, HISTORY_LENGTH / sizeof(short), f) &&
254 load_short(miscHist, MISC_HISTORY_LENGTH / sizeof(short), f) &&
255 load_short((short *)mapBase, mapSizeShort, f) &&
256 (hasMop
257 ? load_short((short *)mopBase, mapSizeShort, f)
258 : true);
259
260 //printf("loadFileData: mapSize: %d historySize: %d mapFileSize: %d mopFileSize: %d size: %ld hasMop: %d isValid: %d result: %d\n", mapSize, historySize, mapFileSize, mopFileSize, size, hasMop, isValid, result);
261
262 if (!hasMop) {
263 memset(mopBase, 0, WORLD_W * WORLD_H * sizeof(short));
264 }
265
266 fclose(f);
267
268 return result;
269}
270
276bool Micropolis::loadFile(const std::string &filename)
277{
278 //printf("loadFile: mapBase: %p\n", mapBase);
279 long n;
280
281 if (!loadFileData(filename)) {
282 return false;
283 }
284
285 /* total funds is a long..... miscHist is array of shorts */
286 /* total funds is being put in the 50th & 51th word of miscHist */
287 /* find the address, cast the ptr to a longPtr, take contents */
288
289 n = *(Quad *)(miscHist + 50);
290 HALF_SWAP_LONGS(&n, 1);
291 setFunds(n);
292
293 n = *(Quad *)(miscHist + 8);
294 HALF_SWAP_LONGS(&n, 1);
295 cityTime = n;
296
297 setAutoBulldoze(miscHist[52] != 0); // flag for autoBulldoze
298 setAutoBudget(miscHist[53] != 0); // flag for autoBudget
299 setAutoGoto(miscHist[54] != 0); // flag for auto-goto
300 setEnableSound(miscHist[55] != 0); // flag for the sound on/off
301 setCityTax(miscHist[56]);
302 //setSpeed(miscHist[57]);
303 setSpeed(3); // Naw let's reset the speed to 3 after loading a city.
304 changeCensus();
305 mustUpdateOptions = true;
306
307 /* yayaya */
308
309 n = *(Quad *)(miscHist + 58);
310 HALF_SWAP_LONGS(&n, 1);
311 policePercent = ((float)n) / ((float)65536);
312
313 n = *(Quad *)(miscHist + 60);
314 HALF_SWAP_LONGS(&n, 1);
315 firePercent = (float)n / (float)65536.0;
316
317 n = *(Quad *)(miscHist + 62);
318 HALF_SWAP_LONGS(&n, 1);
319 roadPercent = (float)n / (float)65536.0;
320
322 (float)(*(Quad*)(miscHist + 58)) /
323 (float)65536.0; /* and 59 */
325 (float)(*(Quad*)(miscHist + 60)) /
326 (float)65536.0; /* and 61 */
328 (float)(*(Quad*)(miscHist + 62)) /
329 (float)65536.0; /* and 63 */
330
331 cityTime = max((Quad)0, cityTime);
332
333 // If the tax is nonsensical, set it to a reasonable value.
334 if (cityTax > 20 || cityTax < 0) {
335 setCityTax(7);
336 }
337
338 // If the speed is nonsensical, set it to a reasonable value.
339 if (simSpeed < 0 || simSpeed > 3) {
340 simSpeed = 3;
341 }
342
343 setSpeed(simSpeed);
344 setPasses(1);
345 initFundingLevel();
346
347 // Set the scenario id to 0.
350 initSimLoad = 1;
351 doInitialEval = false;
352 doSimInit();
354
355 return true;
356}
357
358
364bool Micropolis::saveFile(const std::string &filename)
365{
366 long n;
367 FILE *f;
368
369 if ((f = fopen(filename.c_str(), "wb")) == NULL) {
371 return false;
372 }
373
374 /* total funds is a long..... miscHist is array of ints */
375 /* total funds is bien put in the 50th & 51th word of miscHist */
376 /* find the address, cast the ptr to a longPtr, take contents */
377
378 n = totalFunds;
379 HALF_SWAP_LONGS(&n, 1);
380 (*(Quad *)(miscHist + 50)) = n;
381
382 n = cityTime;
383 HALF_SWAP_LONGS(&n, 1);
384 (*(Quad *)(miscHist + 8)) = n;
385
386 miscHist[52] = autoBulldoze; // flag for autoBulldoze
387 miscHist[53] = autoBudget; // flag for autoBudget
388 miscHist[54] = autoGoto; // flag for auto-goto
389 miscHist[55] = enableSound; // flag for the sound on/off
390 miscHist[57] = simSpeed;
391 miscHist[56] = cityTax; /* post release */
392
393 /* yayaya */
394
395 n = (int)(policePercent * 65536);
396 HALF_SWAP_LONGS(&n, 1);
397 (*(Quad *)(miscHist + 58)) = n;
398
399 n = (int)(firePercent * 65536);
400 HALF_SWAP_LONGS(&n, 1);
401 (*(Quad *)(miscHist + 60)) = n;
402
403 n = (int)(roadPercent * 65536);
404 HALF_SWAP_LONGS(&n, 1);
405 (*(Quad *)(miscHist + 62)) = n;
406
407 bool result =
415 save_short(((short *)&map[0][0]), WORLD_W * WORLD_H, f) &&
416 save_short(((short *)&mop[0][0]), WORLD_W * WORLD_H, f);
417
418 fclose(f);
419
420 return result;
421}
422
423
430{
431 std::string name = NULL;
432 std::string fname = NULL;
433
434 cityFileName = "";
435
437
438 if (s < SC_DULLSVILLE || s > SC_RIO) {
439 s = SC_DULLSVILLE;
440 }
441
442 switch (s) {
443 case SC_DULLSVILLE:
444 name = "Dullsville";
445 fname = "cities/scenario_dullsville.cty";
447 cityTime = ((1900 - 1900) * 48) + 2;
448 setFunds(5000);
449 break;
450 case SC_SAN_FRANCISCO:
451 name = "San Francisco";
452 fname = "cities/scenario_san_francisco.cty";
454 cityTime = ((1906 - 1900) * 48) + 2;
455 setFunds(20000);
456 break;
457 case SC_HAMBURG:
458 name = "Hamburg";
459 fname = "cities/scenario_hamburg.cty";
461 cityTime = ((1944 - 1900) * 48) + 2;
462 setFunds(20000);
463 break;
464 case SC_BERN:
465 name = "Bern";
466 fname = "cities/scenario_bern.cty";
468 cityTime = ((1965 - 1900) * 48) + 2;
469 setFunds(20000);
470 break;
471 case SC_TOKYO:
472 name = "Tokyo";
473 fname = "cities/scenario_tokyo.cty";
475 cityTime = ((1957 - 1900) * 48) + 2;
476 setFunds(20000);
477 break;
478 case SC_DETROIT:
479 name = "Detroit";
480 fname = "cities/scenario_detroit.cty";
482 cityTime = ((1972 - 1900) * 48) + 2;
483 setFunds(20000);
484 break;
485 case SC_BOSTON:
486 name = "Boston";
487 fname = "cities/scenario_boston.cty";
489 cityTime = ((2010 - 1900) * 48) + 2;
490 setFunds(20000);
491 break;
492 case SC_RIO:
493 name = "Rio de Janeiro";
494 fname = "cities/scenario_rio_de_janeiro.cty";
496 cityTime = ((2047 - 1900) * 48) + 2;
497 setFunds(20000);
498 break;
499 default:
500 NOT_REACHED();
501 break;
502 }
503
504 setCleanCityName(name);
505 setSpeed(3);
506 setCityTax(7);
507
508 loadFileData(fname);
509
511 initFundingLevel();
512 updateFunds();
514 initSimLoad = 1;
515 doInitialEval = false;
516 doSimInit();
517 didLoadScenario(s, name, fname);
518}
519
520
522void Micropolis::didLoadScenario(int s, const std::string name, const std::string fname)
523{
524 callback->didLoadScenario(this, callbackVal, name, fname);
525}
526
536bool Micropolis::loadCity(const std::string &filename)
537{
538 if (loadFile(filename)) {
539
540 cityFileName = filename;
541
542 size_t lastSlash = cityFileName.find_last_of('/');
543 size_t pos = (lastSlash == std::string::npos) ? 0 : lastSlash + 1;
544
545 size_t lastDot = cityFileName.find_last_of('.');
546 size_t last =
547 (lastDot == std::string::npos) ? cityFileName.length() : lastDot;
548
549 std::string newCityName = cityFileName.substr(pos, last - pos);
550 setCityName(newCityName);
551
552 doSimInit();
553
554 didLoadCity(filename);
555
556 return true;
557
558 } else {
559
560 didntLoadCity(filename);
561
562 return false;
563
564 }
565}
566
568void Micropolis::didLoadCity(const std::string &filename)
569{
570 callback->didLoadCity(this, callbackVal, filename);
571}
572
573
578void Micropolis::didntLoadCity(const std::string &filename)
579{
580 callback->didntLoadCity(this, callbackVal, filename);
581}
582
583
590{
591 if (cityFileName.length() > 0) {
592
594
595 } else {
596 if (saveFile(cityFileName)) {
597
599
600 } else {
601
603
604 }
605 }
606}
607
608
613void Micropolis::doSaveCityAs(const std::string &filename)
614{
615 callback->saveCityAs(this, callbackVal, filename);
616}
617
618
623void Micropolis::didSaveCity(const std::string &filename)
624{
625 callback->didSaveCity(this, callbackVal, filename);
626}
627
628
633void Micropolis::didntSaveCity(const std::string &filename)
634{
635 callback->didntSaveCity(this, callbackVal, filename);
636}
637
638
646void Micropolis::saveCityAs(const std::string &filename)
647{
648 cityFileName = filename;
649
650 if (saveFile(cityFileName)) {
651
652 size_t lastDot = cityFileName.find_last_of('.');
653 size_t lastSlash = cityFileName.find_last_of('/');
654
655 size_t pos =
656 (lastSlash == std::string::npos) ? 0 : lastSlash + 1;
657 size_t last =
658 (lastDot == std::string::npos) ? cityFileName.length() : lastDot;
659 size_t len =
660 last - pos;
661 std::string newCityName =
662 cityFileName.substr(pos, len);
663
664 setCityName(newCityName);
665
667
668 } else {
669
671
672 }
673}
674
675
float firePercent
bool doInitialEval
Need to perform initial city evaluation.
void didntLoadCity(const std::string &msg)
Definition fileio.cpp:578
bool loadFileData(const std::string &filename)
Definition fileio.cpp:213
void doSaveCityAs(const std::string &filename)
Definition fileio.cpp:613
void didSaveCity(const std::string &filename)
Definition fileio.cpp:623
Scenario scenario
Scenario being played.
short * resHist
void changeCensus()
Definition graph.cpp:122
Callback * callback
Definition micropolis.h:955
void saveCity()
Definition fileio.cpp:589
std::string cityFileName
Filename of the last loaded city.
short * moneyHist
float roadPercent
void loadScenario(Scenario s)
Definition fileio.cpp:429
void didntSaveCity(const std::string &msg)
Definition fileio.cpp:633
void setAutoGoto(bool value)
bool loadFile(const std::string &filename)
Definition fileio.cpp:276
short * crimeHist
short * pollutionHist
std::string cityName
Name of the city.
void setFunds(int dollars)
void updateFunds()
Definition update.cpp:127
void didLoadCity(const std::string &filename)
Definition fileio.cpp:568
void setAutoBulldoze(bool value)
void setEnableSound(bool value)
bool mustUpdateOptions
Options displayed at user need updating.
void saveCityAs(const std::string &filename)
Definition fileio.cpp:646
unsigned short * map[WORLD_W]
short * indHist
short * comHist
bool enableSound
Enable sound.
unsigned short * mapBase
bool saveFile(const std::string &filename)
Definition fileio.cpp:364
void setGameLevel(GameLevel level)
bool autoBudget
void invalidateMaps()
void didLoadScenario(int s, const std::string name, const std::string fname)
Definition fileio.cpp:522
void setCleanCityName(const std::string &name)
void initWillStuff()
bool loadCity(const std::string &filename)
Definition fileio.cpp:536
short cityTax
short * miscHist
float policePercent
Quad totalFunds
Funds of the player.
bool autoBulldoze
void doSimInit()
Definition simulate.cpp:288
void setAutoBudget(bool value)
static bool load_short(short *buf, int len, FILE *f)
Definition fileio.cpp:171
static void half_swap_longs(long *buf, int len)
Definition fileio.cpp:123
#define HALF_SWAP_LONGS(buf, len)
Definition fileio.cpp:99
static void swap_shorts(short *buf, int len)
Definition fileio.cpp:106
#define SWAP_SHORTS(buf, len)
Definition fileio.cpp:92
static bool save_short(short *buf, int len, FILE *f)
Definition fileio.cpp:194
static const int WORLD_H
Definition map_type.h:95
static const int WORLD_W
Definition map_type.h:90
Header file for Micropolis game engine.
Scenario
Definition micropolis.h:698
@ SC_TOKYO
Tokyo (scary monster)
Definition micropolis.h:705
@ SC_NONE
No scenario (free playing)
Definition micropolis.h:699
@ SC_BOSTON
Boston (nuclear meltdown)
Definition micropolis.h:707
@ SC_BERN
Bern (traffic)
Definition micropolis.h:704
@ SC_SAN_FRANCISCO
San francisco (earthquake)
Definition micropolis.h:702
@ SC_RIO
Rio (flooding)
Definition micropolis.h:708
@ SC_HAMBURG
Hamburg (fire bombs)
Definition micropolis.h:703
@ SC_DULLSVILLE
Dullsville (boredom)
Definition micropolis.h:701
@ SC_DETROIT
Detroit (crime)
Definition micropolis.h:706
static const int HISTORY_LENGTH
Definition micropolis.h:210
#define NOT_REACHED()
Definition micropolis.h:848
@ LEVEL_EASY
Simple game level.
Definition micropolis.h:764
static const int MISC_HISTORY_LENGTH
Definition micropolis.h:215
static T max(const T a, const T b)
Definition micropolis.h:796