/* encoding.c: Functions to read the level data. * * Copyright (C) 2001-2006 by Brian Raiter, under the GNU General Public * License. No warranty. See COPYING for details. * * Modified by Yogeshwar Velingker, 2013. */ #ifndef _TINSPIRE #include #include #include #else #include #endif #include "defs.h" #include "state.h" #include "err.h" #include "encoding.h" /* Read a 16-bit value, stored little-endian, from the level data * stream. */ #define readword(p) ((p)[0] | ((p)[1] << 8)) /* Read an x-y coordinate pair from the level data stream. Ensure that * an invalid x value always produces an invalid coordinate. */ #define readpos(x, y) (*(x) < CXGRID ? *(x) + CYGRID * *(y) : CXGRID*CYGRID) /* Translation table for the codes used by the data file to define the * initial state of a level. */ static int const fileids[] = { /* 00 empty space */ Empty, /* 01 wall */ Wall, /* 02 chip */ ICChip, /* 03 water */ Water, /* 04 fire */ Fire, /* 05 invisible wall, perm. */ HiddenWall_Perm, /* 06 blocked north */ Wall_North, /* 07 blocked west */ Wall_West, /* 08 blocked south */ Wall_South, /* 09 blocked east */ Wall_East, /* 0A block */ Block_Static, /* 0B dirt */ Dirt, /* 0C ice */ Ice, /* 0D force south */ Slide_South, /* 0E cloning block N */ crtile(Block, NORTH), /* 0F cloning block W */ crtile(Block, WEST), /* 10 cloning block S */ crtile(Block, SOUTH), /* 11 cloning block E */ crtile(Block, EAST), /* 12 force north */ Slide_North, /* 13 force east */ Slide_East, /* 14 force west */ Slide_West, /* 15 exit */ Exit, /* 16 blue door */ Door_Blue, /* 17 red door */ Door_Red, /* 18 green door */ Door_Green, /* 19 yellow door */ Door_Yellow, /* 1A SE ice slide */ IceWall_Southeast, /* 1B SW ice slide */ IceWall_Southwest, /* 1C NW ice slide */ IceWall_Northwest, /* 1D NE ice slide */ IceWall_Northeast, /* 1E blue block, tile */ BlueWall_Fake, /* 1F blue block, wall */ BlueWall_Real, /* 20 not used */ Overlay_Buffer, /* 21 thief */ Burglar, /* 22 socket */ Socket, /* 23 green button */ Button_Green, /* 24 red button */ Button_Red, /* 25 switch block, closed */ SwitchWall_Closed, /* 26 switch block, open */ SwitchWall_Open, /* 27 brown button */ Button_Brown, /* 28 blue button */ Button_Blue, /* 29 teleport */ Teleport, /* 2A bomb */ Bomb, /* 2B trap */ Beartrap, /* 2C invisible wall, temp. */ HiddenWall_Temp, /* 2D gravel */ Gravel, /* 2E pass once */ PopupWall, /* 2F hint */ HintButton, /* 30 blocked SE */ Wall_Southeast, /* 31 cloning machine */ CloneMachine, /* 32 force all directions */ Slide_Random, /* 33 drowning Chip */ Drowned_Chip, /* 34 burned Chip */ Burned_Chip, /* 35 burned Chip */ Bombed_Chip, /* 36 not used */ HiddenWall_Perm, /* 37 not used */ HiddenWall_Perm, /* 38 not used */ HiddenWall_Perm, /* 39 Chip in exit */ Exited_Chip, /* 3A exit - end game */ Exit_Extra_1, /* 3B exit - end game */ Exit_Extra_2, /* 3C Chip swimming N */ crtile(Swimming_Chip, NORTH), /* 3D Chip swimming W */ crtile(Swimming_Chip, WEST), /* 3E Chip swimming S */ crtile(Swimming_Chip, SOUTH), /* 3F Chip swimming E */ crtile(Swimming_Chip, EAST), /* 40 Bug N */ crtile(Bug, NORTH), /* 41 Bug W */ crtile(Bug, WEST), /* 42 Bug S */ crtile(Bug, SOUTH), /* 43 Bug E */ crtile(Bug, EAST), /* 44 Fireball N */ crtile(Fireball, NORTH), /* 45 Fireball W */ crtile(Fireball, WEST), /* 46 Fireball S */ crtile(Fireball, SOUTH), /* 47 Fireball E */ crtile(Fireball, EAST), /* 48 Pink ball N */ crtile(Ball, NORTH), /* 49 Pink ball W */ crtile(Ball, WEST), /* 4A Pink ball S */ crtile(Ball, SOUTH), /* 4B Pink ball E */ crtile(Ball, EAST), /* 4C Tank N */ crtile(Tank, NORTH), /* 4D Tank W */ crtile(Tank, WEST), /* 4E Tank S */ crtile(Tank, SOUTH), /* 4F Tank E */ crtile(Tank, EAST), /* 50 Glider N */ crtile(Glider, NORTH), /* 51 Glider W */ crtile(Glider, WEST), /* 52 Glider S */ crtile(Glider, SOUTH), /* 53 Glider E */ crtile(Glider, EAST), /* 54 Teeth N */ crtile(Teeth, NORTH), /* 55 Teeth W */ crtile(Teeth, WEST), /* 56 Teeth S */ crtile(Teeth, SOUTH), /* 57 Teeth E */ crtile(Teeth, EAST), /* 58 Walker N */ crtile(Walker, NORTH), /* 59 Walker W */ crtile(Walker, WEST), /* 5A Walker S */ crtile(Walker, SOUTH), /* 5B Walker E */ crtile(Walker, EAST), /* 5C Blob N */ crtile(Blob, NORTH), /* 5D Blob W */ crtile(Blob, WEST), /* 5E Blob S */ crtile(Blob, SOUTH), /* 5F Blob E */ crtile(Blob, EAST), /* 60 Paramecium N */ crtile(Paramecium, NORTH), /* 61 Paramecium W */ crtile(Paramecium, WEST), /* 62 Paramecium S */ crtile(Paramecium, SOUTH), /* 63 Paramecium E */ crtile(Paramecium, EAST), /* 64 Blue key */ Key_Blue, /* 65 Red key */ Key_Red, /* 66 Green key */ Key_Green, /* 67 Yellow key */ Key_Yellow, /* 68 Flippers */ Boots_Water, /* 69 Fire boots */ Boots_Fire, /* 6A Ice skates */ Boots_Ice, /* 6B Suction boots */ Boots_Slide, /* 6C Chip N */ crtile(Chip, NORTH), /* 6D Chip W */ crtile(Chip, WEST), /* 6E Chip S */ crtile(Chip, SOUTH), /* 6F Chip E */ crtile(Chip, EAST) }; /* Initialize the gamestate by reading the level data, in MS dat-file * format, from the state's setup. */ static int expandmsdatlevel(gamestate *state) { gamesetup *setup; unsigned char const *data; unsigned char const *dataend; int size, pos, id; int i, n; memset(state->map, 0, sizeof state->map); state->trapcount = 0; state->clonercount = 0; state->crlistcount = 0; state->hinttext[0] = '\0'; setup = state->game; if (setup->levelsize < 10) goto badlevel; data = setup->leveldata; dataend = data + setup->levelsize; if (readword(data) == 0) goto badlevel; state->chipsneeded = readword(data + 4); if (readword(data + 6) > 1) goto badlevel; size = readword(data + 8); data += 10; if (data + size + 2 > dataend) goto badlevel; for (n = pos = 0 ; n < size && pos < CXGRID * CYGRID ; ++n) { if (data[n] == 0xFF) { i = data[++n]; id = data[++n]; } else { i = 1; id = data[n]; } if (id >= (int)(sizeof fileids / sizeof *fileids)) { id = Wall; state->statusflags |= SF_BADTILES; } else { id = fileids[id]; } while (i-- && pos < CXGRID * CYGRID) state->map[pos++].top.id = id; } if (n < size) warn("level %d: %d extra bytes in upper map layer", setup->number, size - n); else if (pos < CXGRID * CYGRID) warn("level %d: %d missing bytes in upper map layer", setup->number, CXGRID * CYGRID - pos); data += size + 2; size = readword(data - 2); if (data + size > dataend) goto badlevel; for (n = pos = 0 ; n < size && pos < CXGRID * CYGRID ; ++n) { if (data[n] == 0xFF) { i = data[++n]; id = data[++n]; } else { i = 1; id = data[n]; } if (id >= (int)(sizeof fileids / sizeof *fileids)) { id = Wall; state->statusflags |= SF_BADTILES; } else { id = fileids[id]; } while (i-- && pos < CXGRID * CYGRID) state->map[pos++].bot.id = id; } if (n < size) warn("level %d: %d extra bytes in lower map layer", setup->number, size - n); else if (pos < CXGRID * CYGRID) warn("level %d: %d missing bytes in lower map layer", setup->number, CXGRID * CYGRID - pos); data += size; size = readword(data); data += 2; if (data + size != dataend) warn("level %d: inconsistent metadata", setup->number); while (data + 2 < dataend) { size = data[1]; data += 2; if (data + size > dataend) size = dataend - data; switch (data[-2]) { case 1: /* time */ break; case 2: if (size < 2) warn("level %d: ignoring field 2 data of size %d", setup->number, size); else state->chipsneeded = readword(data); break; case 3: /* level name */ break; case 4: if (size % 10) warn("level %d: ignoring %d extra bytes at end of field 4", setup->number, size % 10); state->trapcount = size / 10; for (i = 0 ; i < state->trapcount ; ++i) { state->traps[i].from = readpos(data + i * 10, data + i * 10 + 2); state->traps[i].to = readpos(data + i * 10 + 4, data + i * 10 + 6); } break; case 5: if (size % 8) warn("level %d: ignoring %d extra bytes at end of field 5", setup->number, size % 8); state->clonercount = size / 8; for (i = 0 ; i < state->clonercount ; ++i) { state->cloners[i].from = readpos(data + i * 8, data + i * 8 + 2); state->cloners[i].to = readpos(data + i * 8 + 4, data + i * 8 + 6); } break; case 6: /* passwd */ break; case 7: memcpy(state->hinttext, data, size); state->hinttext[size] = '\0'; break; case 8: /* field 8 passwd */ break; case 10: if (size % 2) warn("level %d: ignoring extra byte at end of field 10", setup->number); state->crlistcount = size / 2; for (i = 0 ; i < state->crlistcount ; ++i) state->crlist[i] = readpos(data + i * 2, data + i * 2 + 1); break; default: warn("level %d: ignoring unrecognized field %d (%d bytes)", setup->number, data[-2], size); break; } data += size; } return TRUE; badlevel: errmsg(NULL, "level %d: invalid data", setup->number); return FALSE; } /* Exported interface. */ int expandleveldata(gamestate *state) { return expandmsdatlevel(state); } /* Return the setup for a small level to display at the completion of * a series. */ void getenddisplaysetup(gamestate *state) { static unsigned char endingdata[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x6D, 0x00, 0x15, 0x39, 0x39, 0x39, 0x15, 0x39, 0x39, 0x15, 0x39, 0xFF, 0x17, 0x00, 0x39, 0x39, 0x15, 0x15, 0x15, 0x39, 0x39, 0x15, 0x39, 0xFF, 0x17, 0x00, 0x39, 0x39, 0x15, 0x15, 0x15, 0xFF, 0x04, 0x39, 0xFF, 0x17, 0x00, 0x15, 0x39, 0x39, 0x39, 0x15, 0x39, 0x39, 0x15, 0x39, 0xFF, 0x17, 0x00, 0xFF, 0x04, 0x15, 0x6E, 0xFF, 0x04, 0x15, 0xFF, 0x17, 0x00, 0xFF, 0x04, 0x39, 0x15, 0x39, 0x39, 0x39, 0x15, 0xFF, 0x17, 0x00, 0x15, 0x39, 0x39, 0x15, 0x15, 0x39, 0x39, 0x15, 0x39, 0xFF, 0x17, 0x00, 0x15, 0x39, 0x39, 0x15, 0x15, 0x39, 0x39, 0x39, 0x15, 0xFF, 0x17, 0x00, 0xFF, 0x04, 0x39, 0x15, 0x39, 0x39, 0x15, 0x15, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xF9, 0x00, 0x10, 0x00, 0xFF, 0x84, 0x00, 0x15, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x7E, 0x00, 0x00, 0x00 }; static gamesetup ending; ending.number = 1; ending.time = 0; ending.besttime = TIME_NIL; ending.sgflags = 0; ending.levelsize = sizeof endingdata; ending.leveldata = endingdata; ending.solutionsize = 0; ending.solutiondata = NULL; strcpy(ending.name, "CONGRATULATIONS!"); ending.passwd[0] = '\0'; state->game = &ending; expandmsdatlevel(state); ending.number = 0; }