📄 loader.c
字号:
/* * NanoBreaker, a Nano-X Breakout clone by Alex Holden. * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is NanoBreaker. * * The Initial Developer of the Original Code is Alex Holden. * Portions created by Alex Holden are Copyright (C) 2002 * Alex Holden <alex@alexholden.net>. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public license (the "[GNU] License"), in which case the * provisions of [GNU] License are applicable instead of those * above. If you wish to allow use of your version of this file only * under the terms of the [GNU] License and not to allow others to use * your version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the [GNU] License. If you do not delete * the provisions above, a recipient may use your version of this file * under either the MPL or the [GNU] License. *//* loader.c mainly consists of routines for loading the game file. It also * contains the high score file loader and saver. */#include <stdio.h>#include <errno.h>#include <ctype.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <nano-X.h>#include <nxcolors.h>#include "nbreaker.h"/* Print a warning indicating that a particular parameter has been defined * more than once, with the line number of the second definition. */static void redefinewarning(char *name, int line, char *gamefile){ fprintf(stderr, "Warning: redefining %s on line %d of game file " "\"%s\"\n", name, line, gamefile);}/* Print a warning indicating that some parameter that is only allowed inside * a level block was encountered outside a level block. */static void notinlevblockerr(char *name, int line, char *gamefile){ fprintf(stderr, "Error: %s while not in a level block on " "line %d of game file \"%s\"\n", name, line, gamefile);}/* Parse a Rows block by reading each line until it reaches a line containing * just EndRows, and convert each row into an appropriate line in the grid of * the specified level (and perform quite a bit of error checking along the * way). Returns 0 on success or 1 on encountering an error. */static int parse_rows(nbstate *state, level *lev, FILE *fp, int *line){ brick *b; int x, y; char buf[256], *p; grid *g = lev->grid; int startline = *line; y = 0; /* Start on the first row. */ /* Read lines from the game file in a loop: */ while(fgets(buf, 256, fp)) { x = 0; /* start the row in the first column. */ *line = *line + 1; /* increment the line number. */ /* Look for the newline at the end of the line. */ if(!(p = strchr(buf, '\n'))) { /* There wasn't one, which probably means that the * line was longer than 255 characters. */ fprintf(stderr, "Too long line on line %d of game file " "\"%s\"\n", *line, state->gamefile); return 1; } *p = 0; /* Get rid of the newline. */ /* If this is the end of the rows block, return "success": */ if(!memcmp(buf, "EndRows", 7)) return 0; /* If we've gone past the number of rows in the grid: */ if(y == state->height) { /* Print an error and return "failure": */ fprintf(stderr, "Too many rows in level definition on" "line %d of game file \"%s\"\n", *line, state->gamefile); return 1; } /* For each letter in the row: */ for(p = buf; *p; p++) { /* Increment the column and if we go past the width of * the grid, print an error and return "failure": */ if(++x > state->width) { fprintf(stderr, "Error: row too long on line " "%d of game file \"%s\" (Width = %d)\n", *line, state->gamefile, state->width); return 1; } if(*p == ' ') b = NULL; /* A space means "no brick". */ else { /* Search through the global brick list for * one with a matching identifier: */ for(b = state->bricks; b && b->identifier != *p; b = b->next); /* If one wasn't found: */ if(!b) { /* Search through the level specific * brick list for one with a matching * identifier: */ for(b = lev->bricks; b && b->identifier != *p; b = b->next); } /* If one wasn't found: */ if(!b) { /* Print an error message and return * "failure": */ fprintf(stderr, "Error: undefined brick" " \"%c\" on line %d of game " "file \"%s\"\n", *p, *line, state->gamefile); return 1; } } /* A matching brick was found if we get to here. * Unless the brick is immutable (which don't count * because they're not normally destroyable), increment * the count of bricks in this level: */ if(b && !(b->flags & BRICK_FLAG_IMMUTABLE)) lev->numbricks++; /* Set the current grid location brick pointer: */ g->b = b; g++; /* Increment to the next grid location. */ } g += state->width - x; /* skip past the rest of the row. */ y++; /* Increment to the next row. */ } /* We shouldn't reach here unless the fgets() fails to get a new line * either because we reach the end of the file or because an I/O error * of some sort occurred. Print an appropriate error message and return * "failure" to the caller: */ if(feof(fp)) { fprintf(stderr, "Error: premature end of file inside rows " "block that started on line %d of game file " "\"%s\"\n", startline, state->gamefile); } else fprintf(stderr, "Error reading from game file \"%s\": %s\n", state->gamefile, strerror(errno)); return 1;}static int parse_powers(nbstate *state, level *lev, FILE *fp, int *line){ int x, y, i; char buf[256], *p; grid *g = lev->grid; int startline = *line; char powers[] = " WSTPNF"; y = 0; /* Start on the first row. */ /* Read lines from the game file in a loop: */ while(fgets(buf, 256, fp)) { x = 0; /* start the row in the first column. */ *line = *line + 1; /* increment the line number. */ /* Look for the newline at the end of the line. */ if(!(p = strchr(buf, '\n'))) { /* There wasn't one, which probably means that the * line was longer than 255 characters. */ fprintf(stderr, "Too long line on line %d of game file " "\"%s\"\n", *line, state->gamefile); return 1; } *p = 0; /* Get rid of the newline. */ /* If this is the end of the powers block, return "success": */ if(!memcmp(buf, "EndPowers", 9)) return 0; /* If we've gone past the number of rows in the grid: */ if(y == state->height) { /* Print an error and return "failure": */ fprintf(stderr, "Too many rows in level definition on" "line %d of game file \"%s\"\n", *line, state->gamefile); return 1; } /* For each letter in the row: */ for(p = buf; *p; p++) { /* Increment the column and if we go past the width of * the grid, print an error and return "failure": */ if(++x > state->width) { fprintf(stderr, "Error: row too long on line " "%d of game file \"%s\" (Width = %d)\n", *line, state->gamefile, state->width); return 1; } /* Search for a power with a matching identifier: */ for(i = 0; powers[i] && powers[i] != *p; i++); /* If we didn't find one print an error message and * return "failure": */ if(!powers[i]) { fprintf(stderr, "Error: invalid power \"%c\" " "on line %d of game file \"%s\"\n", *p, *line, state->gamefile); return 1; } /* Set the power of the current grid location: */ g->power = i - 1; g++; /* Increment to the next grid location. */ } g += state->width - x; /* skip past the rest of the row. */ y++; /* Increment to the next row. */ } /* We shouldn't reach here unless the fgets() fails to get a new line * either because we reach the end of the file or because an I/O error * of some sort occurred. Print an appropriate error message and return * "failure" to the caller: */ if(feof(fp)) { fprintf(stderr, "Error: premature end of file inside powers " "block that started on line %d of game file " "\"%s\"\n", startline, state->gamefile); } else fprintf(stderr, "Error reading from game file \"%s\": %s\n", state->gamefile, strerror(errno)); return 1;}/* Parse a brick definition line. */static int parse_brick(nbstate *state, int inlevel, level *lev, int line, char *buf){ int i; char *flags, f; brick *b = NULL; /* Search for an existing brick with the same identifier in the global * brick list: */ for(b = state->bricks; b && b->identifier != *buf; b = b->next); /* If we didn't find one and we're in a level definition, search for * a matching brick in the level specific brick list: */ if(!b && inlevel) for(b = lev->bricks; b && b->identifier != *buf; b = b->next); /* If we have found an existing brick with the same identifier, print * a warning and return: */ if(b) { fprintf(stderr, "Warning: ignoring duplicate brick definition " "on line %d of game file \"%s\"\n", line, state->gamefile); return 0; } /* Allocate the new brick structure: */ if(!(b = malloc(sizeof(brick)))) { oom(); return 1; } b->identifier = *buf; /* Set the brick identifier. */ buf += 2; /* Skip past the identifier and the space after it. */ /* Look for a space after the filename, and if one is found set it to * 0 and set flags to the character after the space. After this, buf * points to a null terminated string which is the image filename, and * flags is either NULL if there was no space (and no flags) after the * filename, or a pointer to a null terminated list of flags. */ if((flags = strchr(buf, ' '))) *flags++ = 0; b->flags = 0; /* Initialise the flags with all of them turned off. */ /* If there is a flags string: */ if(flags) { /* For each flag in the string: */ while((f = *flags++)) { /* Set the flag which matches this letter: */ if(f == 'I') b->flags |= BRICK_FLAG_IMMUTABLE; else if(f == '2') b->flags |= BRICK_FLAG_2_HITS; else if(f == '3') b->flags |= BRICK_FLAG_3_HITS; else if(f == 'S') b->flags |= BRICK_FLAG_SMALL_BONUS; else if(f == 'M') b->flags |= BRICK_FLAG_MEDIUM_BONUS; else if(f == 'L') b->flags |= BRICK_FLAG_LARGE_BONUS; else if(f == 'H') b->flags |= BRICK_FLAG_HUGE_BONUS; else { /* The letter wasn't one of the above, so print * a warning message and ignore the flag. */ fprintf(stderr, "Warning: ignoring invalid " "flag \"%c\" specified for " "brick \"%c\" on line %d " "of game file \"%s\"\n", f, b->identifier, line, state->gamefile); } } } /* Count the number of flags which affect the number of times you have * to hit the brick to destroy it: */ i = 0; if(b->flags & BRICK_FLAG_IMMUTABLE) i++; if(b->flags & BRICK_FLAG_2_HITS) i++; if(b->flags & BRICK_FLAG_3_HITS) i++; /* If there was more than one of the above flags set, print a warning * and cancel all of them: */ if(i > 1) { fprintf(stderr, "Warning: brick flags I, 2, and 3 are mutually " "exclusive (see brick \"%c\" on line %d of " "game file \"%s\")\n", b->identifier, line, state->gamefile); b->flags &= ~(BRICK_FLAG_IMMUTABLE | BRICK_FLAG_2_HITS | BRICK_FLAG_3_HITS); } /* Count the number of flags which tell what bonus is associated with * this brick: */ i = 0; if(b->flags & BRICK_FLAG_SMALL_BONUS) i++; if(b->flags & BRICK_FLAG_MEDIUM_BONUS) i++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -