Annotation of brogue-ce/src/platform/platformdependent.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * platformdependent.c
! 3: * Brogue
! 4: *
! 5: * Created by Brian Walker on 4/13/10.
! 6: * Copyright 2010. All rights reserved.
! 7: *
! 8: * This file is part of Brogue.
! 9: *
! 10: * Brogue is free software: you can redistribute it and/or modify
! 11: * it under the terms of the GNU General Public License as published by
! 12: * the Free Software Foundation, either version 3 of the License, or
! 13: * (at your option) any later version.
! 14: *
! 15: * Brogue is distributed in the hope that it will be useful,
! 16: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 17: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 18: * GNU General Public License for more details.
! 19: *
! 20: * You should have received a copy of the GNU General Public License
! 21: * along with Brogue. If not, see <http://www.gnu.org/licenses/>.
! 22: */
! 23:
! 24: #include <ctype.h>
! 25: #include <stdio.h>
! 26: #include <string.h>
! 27: #include <time.h>
! 28: #include <sys/types.h>
! 29: #include <sys/stat.h>
! 30: #include <dirent.h>
! 31:
! 32: #include "platform.h"
! 33:
! 34: typedef struct brogueScoreEntry {
! 35: long int score;
! 36: long int dateNumber; // in seconds
! 37: char dateText[COLS]; // in the form mm/dd/yy
! 38: char description[COLS];
! 39: } brogueScoreEntry;
! 40:
! 41: brogueScoreEntry scoreBuffer[HIGH_SCORES_COUNT];
! 42:
! 43: unsigned int glyphToUnicode(enum displayGlyph glyph) {
! 44: if (glyph < 128) return glyph;
! 45:
! 46: switch (glyph) {
! 47: case G_UP_ARROW: return U_UP_ARROW;
! 48: case G_DOWN_ARROW: return U_DOWN_ARROW;
! 49: case G_POTION: return '!';
! 50: case G_GRASS: return '"';
! 51: case G_WALL: return '#';
! 52: case G_DEMON: return '&';
! 53: case G_OPEN_DOOR: return '\'';
! 54: case G_GOLD: return '*';
! 55: case G_CLOSED_DOOR: return '+';
! 56: case G_RUBBLE: return ',';
! 57: case G_KEY: return '-';
! 58: case G_BOG: return '~';
! 59: case G_CHAIN_TOP_LEFT:
! 60: case G_CHAIN_BOTTOM_RIGHT:
! 61: return '\\';
! 62: case G_CHAIN_TOP_RIGHT:
! 63: case G_CHAIN_BOTTOM_LEFT:
! 64: return '/';
! 65: case G_CHAIN_TOP:
! 66: case G_CHAIN_BOTTOM:
! 67: return '|';
! 68: case G_CHAIN_LEFT:
! 69: case G_CHAIN_RIGHT:
! 70: return '-';
! 71: case G_FOOD: return ';';
! 72: case G_UP_STAIRS: return '<';
! 73: case G_VENT: return '=';
! 74: case G_DOWN_STAIRS: return '>';
! 75: case G_PLAYER: return '@';
! 76: case G_BOG_MONSTER: return 'B';
! 77: case G_CENTAUR: return 'C';
! 78: case G_DRAGON: return 'D';
! 79: case G_FLAMEDANCER: return 'F';
! 80: case G_GOLEM: return 'G';
! 81: case G_TENTACLE_HORROR: return 'H';
! 82: case G_IFRIT: return 'I';
! 83: case G_JELLY: return 'J';
! 84: case G_KRAKEN: return 'K';
! 85: case G_LICH: return 'L';
! 86: case G_NAGA: return 'N';
! 87: case G_OGRE: return 'O';
! 88: case G_PHANTOM: return 'P';
! 89: case G_REVENANT: return 'R';
! 90: case G_SALAMANDER: return 'S';
! 91: case G_TROLL: return 'T';
! 92: case G_UNDERWORM: return 'U';
! 93: case G_VAMPIRE: return 'V';
! 94: case G_WRAITH: return 'W';
! 95: case G_ZOMBIE: return 'Z';
! 96: case G_ARMOR: return '[';
! 97: case G_STAFF: return '/';
! 98: case G_WEB: return ':';
! 99: case G_MOUND: return 'a';
! 100: case G_BLOAT: return 'b';
! 101: case G_CENTIPEDE: return 'c';
! 102: case G_DAR_BLADEMASTER: return 'd';
! 103: case G_EEL: return 'e';
! 104: case G_FURY: return 'f';
! 105: case G_GOBLIN: return 'g';
! 106: case G_IMP: return 'i';
! 107: case G_JACKAL: return 'j';
! 108: case G_KOBOLD: return 'k';
! 109: case G_MONKEY: return 'm';
! 110: case G_PIXIE: return 'p';
! 111: case G_RAT: return 'r';
! 112: case G_SPIDER: return 's';
! 113: case G_TOAD: return 't';
! 114: case G_BAT: return 'v';
! 115: case G_WISP: return 'w';
! 116: case G_PHOENIX: return 'P';
! 117: case G_ALTAR: return '|';
! 118: case G_LIQUID: return '~';
! 119: case G_FLOOR: return U_MIDDLE_DOT;
! 120: case G_CHASM: return U_FOUR_DOTS;
! 121: case G_TRAP: return U_DIAMOND;
! 122: case G_FIRE: return U_FLIPPED_V;
! 123: case G_FOLIAGE: return U_ARIES;
! 124: case G_AMULET: return U_ANKH;
! 125: case G_SCROLL: return U_MUSIC_NOTE;
! 126: case G_RING: return U_CIRCLE;
! 127: case G_WEAPON: return U_UP_ARROW;
! 128: case G_GEM: return U_FILLED_CIRCLE;
! 129: case G_TOTEM: return U_NEUTER;
! 130: case G_GOOD_MAGIC: return U_FILLED_CIRCLE_BARS;
! 131: case G_BAD_MAGIC: return U_CIRCLE_BARS;
! 132: case G_DOORWAY: return U_OMEGA;
! 133: case G_CHARM: return U_LIGHTNING_BOLT;
! 134: case G_WALL_TOP: return '#';
! 135: case G_DAR_PRIESTESS: return 'd';
! 136: case G_DAR_BATTLEMAGE: return 'd';
! 137: case G_GOBLIN_MAGIC: return 'g';
! 138: case G_GOBLIN_CHIEFTAN: return 'g';
! 139: case G_OGRE_MAGIC: return 'O';
! 140: case G_GUARDIAN: return U_ESZETT;
! 141: case G_WINGED_GUARDIAN: return U_ESZETT;
! 142: case G_EGG: return U_FILLED_CIRCLE;
! 143: case G_WARDEN: return 'Y';
! 144: case G_DEWAR: return '&';
! 145: case G_ANCIENT_SPIRIT: return 'M';
! 146: case G_LEVER: return '/';
! 147: case G_LEVER_PULLED: return '\\';
! 148: case G_BLOODWORT_STALK: return U_ARIES;
! 149: case G_FLOOR_ALT: return U_MIDDLE_DOT;
! 150: case G_UNICORN: return U_U_ACUTE;
! 151: case G_TURRET: return U_FILLED_CIRCLE;
! 152: case G_WAND: return '~';
! 153: case G_GRANITE: return '#';
! 154: case G_CARPET: return U_MIDDLE_DOT;
! 155: case G_CLOSED_IRON_DOOR: return '+';
! 156: case G_OPEN_IRON_DOOR: return '\'';
! 157: case G_TORCH: return '#';
! 158: case G_CRYSTAL: return '#';
! 159: case G_PORTCULLIS: return '#';
! 160: case G_BARRICADE: return '#';
! 161: case G_STATUE: return U_ESZETT;
! 162: case G_CRACKED_STATUE: return U_ESZETT;
! 163: case G_CLOSED_CAGE: return '#';
! 164: case G_OPEN_CAGE: return '|';
! 165: case G_PEDESTAL: return '|';
! 166: case G_CLOSED_COFFIN: return '-';
! 167: case G_OPEN_COFFIN: return '-';
! 168: case G_MAGIC_GLYPH: return U_FOUR_DOTS;
! 169: case G_BRIDGE: return '=';
! 170: case G_BONES: return ',';
! 171: case G_ELECTRIC_CRYSTAL: return U_CURRENCY;
! 172: case G_ASHES: return '\'';
! 173: case G_BEDROLL: return '=';
! 174: case G_BLOODWORT_POD: return '*';
! 175: case G_VINE: return ':';
! 176: case G_NET: return ':';
! 177: case G_LICHEN: return '"';
! 178: case G_PIPES: return '+';
! 179: case G_SAC_ALTAR: return '|';
! 180: case G_ORB_ALTAR: return '|';
! 181:
! 182: default:
! 183: brogueAssert(false);
! 184: return '?';
! 185: }
! 186: }
! 187:
! 188:
! 189: void plotChar(enum displayGlyph inputChar,
! 190: short xLoc, short yLoc,
! 191: short foreRed, short foreGreen, short foreBlue,
! 192: short backRed, short backGreen, short backBlue) {
! 193: currentConsole.plotChar(inputChar, xLoc, yLoc, foreRed, foreGreen, foreBlue, backRed, backGreen, backBlue);
! 194: }
! 195:
! 196: void pausingTimerStartsNow() {
! 197:
! 198: }
! 199:
! 200: boolean shiftKeyIsDown() {
! 201: return currentConsole.modifierHeld(0);
! 202: }
! 203: boolean controlKeyIsDown() {
! 204: return currentConsole.modifierHeld(1);
! 205: }
! 206:
! 207: void nextKeyOrMouseEvent(rogueEvent *returnEvent, boolean textInput, boolean colorsDance) {
! 208: currentConsole.nextKeyOrMouseEvent(returnEvent, textInput, colorsDance);
! 209: }
! 210:
! 211: boolean pauseForMilliseconds(short milliseconds) {
! 212: return currentConsole.pauseForMilliseconds(milliseconds);
! 213: }
! 214:
! 215: void notifyEvent(short eventId, int data1, int data2, const char *str1, const char *str2) {
! 216: if (currentConsole.notifyEvent) {
! 217: currentConsole.notifyEvent(eventId, data1, data2, str1, str2);
! 218: }
! 219: }
! 220:
! 221: boolean takeScreenshot() {
! 222: if (currentConsole.takeScreenshot) {
! 223: return currentConsole.takeScreenshot();
! 224: } else {
! 225: return false;
! 226: }
! 227: }
! 228:
! 229: boolean setGraphicsEnabled(boolean state) {
! 230: if (currentConsole.setGraphicsEnabled) {
! 231: return currentConsole.setGraphicsEnabled(state);
! 232: } else {
! 233: return false;
! 234: }
! 235: }
! 236:
! 237: // creates an empty high scores file
! 238: void initScores() {
! 239: short i;
! 240: FILE *scoresFile;
! 241:
! 242: scoresFile = fopen("BrogueHighScores.txt", "w");
! 243: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 244: fprintf(scoresFile, "%li\t%li\t%s", (long) 0, (long) 0, "(empty entry)\n");
! 245: }
! 246: fclose(scoresFile);
! 247: }
! 248:
! 249: // sorts the entries of the scoreBuffer global variable by score in descending order;
! 250: // returns the sorted line number of the most recent entry
! 251: short sortScoreBuffer() {
! 252: short i, j, highestUnsortedLine, mostRecentSortedLine = 0;
! 253: long highestUnsortedScore, mostRecentDate;
! 254: brogueScoreEntry sortedScoreBuffer[HIGH_SCORES_COUNT];
! 255: boolean lineSorted[HIGH_SCORES_COUNT];
! 256:
! 257: mostRecentDate = 0;
! 258:
! 259: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 260: lineSorted[i] = false;
! 261: }
! 262:
! 263: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 264: highestUnsortedLine = 0;
! 265: highestUnsortedScore = 0;
! 266: for (j=0; j<HIGH_SCORES_COUNT; j++) {
! 267: if (!lineSorted[j] && scoreBuffer[j].score >= highestUnsortedScore) {
! 268: highestUnsortedLine = j;
! 269: highestUnsortedScore = scoreBuffer[j].score;
! 270: }
! 271: }
! 272: sortedScoreBuffer[i] = scoreBuffer[highestUnsortedLine];
! 273: lineSorted[highestUnsortedLine] = true;
! 274: }
! 275:
! 276: // copy the sorted list back into scoreBuffer, remember the most recent entry
! 277: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 278: scoreBuffer[i] = sortedScoreBuffer[i];
! 279: if (scoreBuffer[i].dateNumber > mostRecentDate) {
! 280: mostRecentDate = scoreBuffer[i].dateNumber;
! 281: mostRecentSortedLine = i;
! 282: }
! 283: }
! 284: return mostRecentSortedLine;
! 285: }
! 286:
! 287: // loads the BrogueHighScores.txt file into the scoreBuffer global variable
! 288: // score file format is: score, tab, date in seconds, tab, description, newline.
! 289: short loadScoreBuffer() {
! 290: short i;
! 291: FILE *scoresFile;
! 292: time_t rawtime;
! 293: struct tm * timeinfo;
! 294:
! 295: scoresFile = fopen("BrogueHighScores.txt", "r");
! 296:
! 297: if (scoresFile == NULL) {
! 298: initScores();
! 299: scoresFile = fopen("BrogueHighScores.txt", "r");
! 300: }
! 301:
! 302: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 303: // load score and also the date in seconds
! 304: fscanf(scoresFile, "%li\t%li\t", &(scoreBuffer[i].score), &(scoreBuffer[i].dateNumber));
! 305:
! 306: // load description
! 307: fgets(scoreBuffer[i].description, COLS, scoresFile);
! 308: // strip the newline off the end
! 309: scoreBuffer[i].description[strlen(scoreBuffer[i].description) - 1] = '\0';
! 310:
! 311: // convert date to DATE_FORMAT
! 312: rawtime = (time_t) scoreBuffer[i].dateNumber;
! 313: timeinfo = localtime(&rawtime);
! 314: strftime(scoreBuffer[i].dateText, DCOLS, DATE_FORMAT, timeinfo);
! 315: }
! 316: fclose(scoresFile);
! 317: return sortScoreBuffer();
! 318: }
! 319:
! 320: void loadKeymap() {
! 321: int i;
! 322: FILE *f;
! 323: char buffer[512];
! 324:
! 325: f = fopen("keymap.txt", "r");
! 326:
! 327: if (f != NULL) {
! 328: while (fgets(buffer, 512, f) != NULL) {
! 329: // split it in two (destructively)
! 330: int mode = 1;
! 331: char *input_name = NULL, *output_name = NULL;
! 332: for (i = 0; buffer[i]; i++) {
! 333: if (isspace(buffer[i])) {
! 334: buffer[i] = '\0';
! 335: mode = 1;
! 336: } else {
! 337: if (mode) {
! 338: if (input_name == NULL) input_name = buffer + i;
! 339: else if (output_name == NULL) output_name = buffer + i;
! 340: }
! 341: mode = 0;
! 342: }
! 343: }
! 344: if (input_name != NULL && output_name != NULL) {
! 345: if (input_name[0] == '#') continue; // must be a comment
! 346:
! 347: currentConsole.remap(input_name, output_name);
! 348: }
! 349: }
! 350: fclose(f);
! 351: }
! 352: }
! 353:
! 354:
! 355: // saves the scoreBuffer global variable into the BrogueHighScores.txt file,
! 356: // thus overwriting whatever is already there.
! 357: // The numerical version of the date is what gets saved; the "mm/dd/yy" version is ignored.
! 358: // Does NOT do any sorting.
! 359: void saveScoreBuffer() {
! 360: short i;
! 361: FILE *scoresFile;
! 362:
! 363: scoresFile = fopen("BrogueHighScores.txt", "w");
! 364:
! 365: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 366: // save the entry
! 367: fprintf(scoresFile, "%li\t%li\t%s\n", scoreBuffer[i].score, scoreBuffer[i].dateNumber, scoreBuffer[i].description);
! 368: }
! 369:
! 370: fclose(scoresFile);
! 371: }
! 372:
! 373: void dumpScores() {
! 374: int i;
! 375:
! 376: rogueHighScoresEntry list[HIGH_SCORES_COUNT];
! 377: getHighScoresList(list);
! 378:
! 379: for (i = 0; i < HIGH_SCORES_COUNT; i++) {
! 380: if (list[i].score > 0) {
! 381: printf("%d\t%s\t%s\n", (int) list[i].score, list[i].date, list[i].description);
! 382: }
! 383: }
! 384: }
! 385:
! 386: short getHighScoresList(rogueHighScoresEntry returnList[HIGH_SCORES_COUNT]) {
! 387: short i, mostRecentLineNumber;
! 388:
! 389: mostRecentLineNumber = loadScoreBuffer();
! 390:
! 391: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 392: returnList[i].score = scoreBuffer[i].score;
! 393: strcpy(returnList[i].date, scoreBuffer[i].dateText);
! 394: strcpy(returnList[i].description, scoreBuffer[i].description);
! 395: }
! 396:
! 397: return mostRecentLineNumber;
! 398: }
! 399:
! 400: boolean saveHighScore(rogueHighScoresEntry theEntry) {
! 401: short i, lowestScoreIndex = -1;
! 402: long lowestScore = -1;
! 403:
! 404: loadScoreBuffer();
! 405:
! 406: for (i=0; i<HIGH_SCORES_COUNT; i++) {
! 407: if (scoreBuffer[i].score < lowestScore || i == 0) {
! 408: lowestScore = scoreBuffer[i].score;
! 409: lowestScoreIndex = i;
! 410: }
! 411: }
! 412:
! 413: if (lowestScore > theEntry.score) {
! 414: return false;
! 415: }
! 416:
! 417: scoreBuffer[lowestScoreIndex].score = theEntry.score;
! 418: scoreBuffer[lowestScoreIndex].dateNumber = (long) time(NULL);
! 419: strcpy(scoreBuffer[lowestScoreIndex].description, theEntry.description);
! 420:
! 421: saveScoreBuffer();
! 422:
! 423: return true;
! 424: }
! 425:
! 426: // start of file listing
! 427:
! 428: struct filelist {
! 429: fileEntry *files;
! 430: char *names;
! 431:
! 432: int nfiles, maxfiles;
! 433: int nextname, maxname;
! 434: };
! 435:
! 436: struct filelist *newFilelist() {
! 437: struct filelist *list = malloc(sizeof(*list));
! 438:
! 439: list->nfiles = 0;
! 440: list->nextname = 0;
! 441: list->maxfiles = 64;
! 442: list->maxname = list->maxfiles * 64;
! 443:
! 444: list->files = malloc(sizeof(fileEntry) * list->maxfiles);
! 445: list->names = malloc(list->maxname);
! 446:
! 447: return list;
! 448: }
! 449:
! 450: fileEntry *addfile(struct filelist *list, const char *name) {
! 451: int len = strlen(name);
! 452: if (len + list->nextname >= list->maxname) {
! 453: int newmax = (list->maxname + len) * 2;
! 454: char *newnames = realloc(list->names, newmax);
! 455: if (newnames != NULL) {
! 456: list->names = newnames;
! 457: list->maxname = newmax;
! 458: } else {
! 459: // fail silently
! 460: return NULL;
! 461: }
! 462: }
! 463:
! 464: if (list->nfiles >= list->maxfiles) {
! 465: int newmax = list->maxfiles * 2;
! 466: fileEntry *newfiles = realloc(list->files, sizeof(fileEntry) * newmax);
! 467: if (newfiles != NULL) {
! 468: list->files = newfiles;
! 469: list->maxfiles = newmax;
! 470: } else {
! 471: // fail silently
! 472: return NULL;
! 473: }
! 474: }
! 475:
! 476: // add the new file and copy the name into the buffer
! 477: list->files[list->nfiles].path = ((char *) NULL) + list->nextname; // don't look at them until they are transferred out
! 478: list->files[list->nfiles].date = (struct tm) {0}; // associate a dummy date (1899-12-31) to avoid random data, it will be correctly populated when using listFiles()
! 479:
! 480: strncpy(list->names + list->nextname, name, len + 1);
! 481:
! 482: list->nextname += len + 1;
! 483: list->nfiles += 1;
! 484:
! 485: return list->files + (list->nfiles - 1);
! 486: }
! 487:
! 488: void freeFilelist(struct filelist *list) {
! 489: //if (list->names != NULL) free(list->names);
! 490: //if (list->files != NULL) free(list->files);
! 491: free(list);
! 492: }
! 493:
! 494: fileEntry *commitFilelist(struct filelist *list, char **namebuffer) {
! 495: int i;
! 496: /*fileEntry *files = malloc(list->nfiles * sizeof(fileEntry) + list->nextname); // enough space for all the names and all the files
! 497:
! 498: if (files != NULL) {
! 499: char *names = (char *) (files + list->nfiles);
! 500:
! 501: for (i=0; i < list->nfiles; i++) {
! 502: files[i] = list->files[i];
! 503: files[i].path = names + (files[i].path - (char *) NULL);
! 504: }
! 505:
! 506: memcpy(names, list->names, list->nextname);
! 507: }
! 508: */
! 509: for (i=0; i < list->nfiles; i++) {
! 510: list->files[i].path = list->names + (list->files[i].path - (char *) NULL);
! 511: }
! 512: *namebuffer = list->names;
! 513:
! 514: return list->files;
! 515: }
! 516:
! 517: fileEntry *listFiles(short *fileCount, char **namebuffer) {
! 518: struct filelist *list = newFilelist();
! 519:
! 520: // windows: FindFirstFile/FindNextFile
! 521: DIR *dp= opendir ("./");
! 522:
! 523: if (dp != NULL) {
! 524: struct dirent *ep;
! 525: struct stat statbuf;
! 526: struct tm *timeinfo;
! 527:
! 528: while ((ep = readdir(dp))) {
! 529: // get statistics about the file (0 on success)
! 530: if (!stat(ep->d_name, &statbuf)) {
! 531: fileEntry *file = addfile(list, ep->d_name);
! 532: if (file != NULL) {
! 533: // add the modification date to the file entry
! 534: timeinfo = localtime(&statbuf.st_mtime);
! 535: file->date = *timeinfo;
! 536: }
! 537: }
! 538: }
! 539:
! 540: closedir (dp);
! 541: }
! 542: else {
! 543: *fileCount = 0;
! 544: return NULL;
! 545: }
! 546:
! 547: fileEntry *files = commitFilelist(list, namebuffer);
! 548:
! 549: if (files != NULL) {
! 550: *fileCount = (short) list->nfiles;
! 551: } else {
! 552: *fileCount = 0;
! 553: }
! 554:
! 555: freeFilelist(list);
! 556:
! 557: return files;
! 558: }
! 559:
! 560: // end of file listing
! 561:
! 562: void initializeLaunchArguments(enum NGCommands *command, char *path, unsigned long *seed) {
! 563: // we've actually already done this at this point, except for the seed.
! 564: }
! 565:
! 566: boolean isApplicationActive(void) {
! 567: // FIXME: finish
! 568: return true;
! 569: }
! 570:
CVSweb