aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWinterGrascph2016-05-07 20:53:35 +0200
committerBendegúz Nagy2016-08-26 23:02:22 +0200
commit4f9182507afb365638b33341fb797d82c3a50df0 (patch)
tree1bb1a2abdf6bd899a1c5855ccb2f29890acd222a
parenta8434fc165266fb6a788618cdf334b01be53c25e (diff)
downloadscummvm-rg350-4f9182507afb365638b33341fb797d82c3a50df0.tar.gz
scummvm-rg350-4f9182507afb365638b33341fb797d82c3a50df0.tar.bz2
scummvm-rg350-4f9182507afb365638b33341fb797d82c3a50df0.zip
DM: Implement Dungeon.dat file parsing, add relevant structures
-rw-r--r--engines/dm/dungeonman.cpp227
-rw-r--r--engines/dm/dungeonman.h125
-rw-r--r--engines/dm/gfx.cpp2
3 files changed, 341 insertions, 13 deletions
diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index 292ca023e7..e33895db5a 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -1,20 +1,49 @@
#include "dungeonman.h"
#include "common/file.h"
+#include "common/memstream.h"
+
namespace DM {
-DungeonMan::DungeonMan(DMEngine *dmEngine) : _vm(dmEngine), _dungeonDataSize(0), _dungeonData(NULL) {}
+// TODO: refactor direction into a class
+int8 dirIntoStepCountEast[4] = {0 /* North */, 1 /* East */, 0 /* West */, -1 /* South */};
+int8 dirIntoStepCountNorth[4] = {-1 /* North */, 0 /* East */, 1 /* West */, 0 /* South */};
+
+void turnDirRight(direction &dir) { dir = (direction)((dir + 1) & 3); }
+
+
+}
+
+using namespace DM;
+
+
+void DungeonMan::mapCoordsAfterRelMovement(direction dir, uint16 stepsForward, uint16 stepsRight, uint16 &posX, uint16 &posY) {
+ posX += dirIntoStepCountEast[dir] * stepsForward;
+ posY += dirIntoStepCountNorth[dir] * stepsForward;
+ turnDirRight(dir);
+ posX += dirIntoStepCountEast[dir] * stepsRight;
+ posY += dirIntoStepCountNorth[dir] * stepsRight;
+}
+
+DungeonMan::DungeonMan(DMEngine *dmEngine) : _vm(dmEngine), _rawDunFileData(NULL), _maps(NULL), _rawMapData(NULL) {}
DungeonMan::~DungeonMan() {
- delete[] _dungeonData;
+ delete[] _rawDunFileData;
+ delete[] _maps;
+ delete[] _dunData.dunMapsFirstColumnIndex;
+ delete[] _dunData.dunColumnsCumulativeSquareThingCount;
+ delete[] _dunData.squareFirstThings;
+ delete[] _dunData.dunTextData;
+ delete[] _dunData.dungeonMapData;
}
-void DungeonMan::loadDungeonFile() {
+void DungeonMan::decompressDungeonFile() {
Common::File f;
f.open("Dungeon.dat");
if (f.readUint16BE() == 0x8104) {
- _dungeonDataSize = f.readUint32BE();
- _dungeonData = new byte[_dungeonDataSize];
+ _rawDunFileDataSize = f.readUint32BE();
+ if (_rawDunFileData) delete[] _rawDunFileData;
+ _rawDunFileData = new byte[_rawDunFileDataSize];
f.readUint16BE();
byte common[4];
for (uint16 i = 0; i < 4; ++i)
@@ -29,7 +58,7 @@ void DungeonMan::loadDungeonFile() {
uint16 wordBuff = f.readUint16BE();
uint8 bitsLeftInByte = 8;
byte byteBuff = f.readByte();
- while (uncompIndex < _dungeonDataSize) {
+ while (uncompIndex < _rawDunFileDataSize) {
while (bitsUsedInWord != 0) {
uint8 shiftVal;
if (f.eos()) {
@@ -49,18 +78,198 @@ void DungeonMan::loadDungeonFile() {
bitsUsedInWord -= shiftVal;
}
if (((wordBuff >> 15) & 1) == 0) {
- _dungeonData[uncompIndex++] = common[(wordBuff >> 13) & 3];
+ _rawDunFileData[uncompIndex++] = common[(wordBuff >> 13) & 3];
bitsUsedInWord += 3;
} else if (((wordBuff >> 14) & 3) == 2) {
- _dungeonData[uncompIndex++] = lessCommon[(wordBuff >> 10) & 15];
+ _rawDunFileData[uncompIndex++] = lessCommon[(wordBuff >> 10) & 15];
bitsUsedInWord += 6;
} else if (((wordBuff >> 14) & 3) == 3) {
- _dungeonData[uncompIndex++] = (wordBuff >> 6) & 255;
+ _rawDunFileData[uncompIndex++] = (wordBuff >> 6) & 255;
bitsUsedInWord += 10;
}
}
+ } else {
+ // TODO: if the dungeon is uncompressed, read it here
}
f.close();
}
+
+uint8 gAdditionalThingCounts[16] = {
+ 0, /* Door */
+ 0, /* Teleporter */
+ 0, /* Text String */
+ 0, /* Sensor */
+ 75, /* Group */
+ 100, /* Weapon */
+ 120, /* Armour */
+ 0, /* Scroll */
+ 5, /* Potion */
+ 0, /* Container */
+ 140, /* Junk */
+ 0, /* Unused */
+ 0, /* Unused */
+ 0, /* Unused */
+ 60, /* Projectile */
+ 50 /* Explosion */
+}; // @ G0236_auc_Graphic559_AdditionalThingCounts
+
+// TODO: refactor THINGS into classes
+unsigned char gThingDataByteCount[16] = {
+ 4, /* Door */
+ 6, /* Teleporter */
+ 4, /* Text String */
+ 8, /* Sensor */
+ 16, /* Group */
+ 4, /* Weapon */
+ 4, /* Armour */
+ 4, /* Scroll */
+ 4, /* Potion */
+ 8, /* Container */
+ 4, /* Junk */
+ 0, /* Unused */
+ 0, /* Unused */
+ 0, /* Unused */
+ 8, /* Projectile */
+ 4 /* Explosion */
+}; // @ G0235_auc_Graphic559_ThingDataByteCount
+
+const Thing Thing::specThingNone(0, 0, 0);
+
+
+void DungeonMan::loadDungeonFile() {
+ if (_messages.newGame)
+ decompressDungeonFile();
+
+ Common::MemoryReadStream dunDataStream(_rawDunFileData, _fileHeader.rawMapDataSize, DisposeAfterUse::NO);
+
+ // initialize _fileHeader
+ _fileHeader.dungeonId = _fileHeader.ornamentRandomSeed = dunDataStream.readUint16BE();
+ _fileHeader.rawMapDataSize = dunDataStream.readUint16BE();
+ _fileHeader.mapCount = dunDataStream.readByte();
+ dunDataStream.readByte(); // discard 1 byte
+ _fileHeader.textDataWordCount = dunDataStream.readUint16BE();
+ uint16 partyPosition = dunDataStream.readUint16BE();
+ _fileHeader.partyStartDir = (direction)((partyPosition >> 10) & 3);
+ _fileHeader.partyStartPosY = (partyPosition >> 5) & 0x1F;
+ _fileHeader.partyStartPosX = (partyPosition >> 0) & 0x1F;
+ _fileHeader.squareFirstThingCount = dunDataStream.readUint16BE();
+ for (uint16 i = 0; i < kThingTypeTotal; ++i)
+ _fileHeader.thingCounts[i] = dunDataStream.readUint16BE();
+
+ // init party position and mapindex
+ if (_messages.newGame) {
+ _dunData.partyDir = _fileHeader.partyStartDir;
+ _dunData.partyPosX = _fileHeader.partyStartPosX;
+ _dunData.partyPosY = _fileHeader.partyStartPosY;
+ _dunData.currMapIndex = 0;
+ }
+
+ // load map data
+ if (_maps) delete[] _maps;
+
+ _maps = new Map[_fileHeader.mapCount];
+ for (uint16 i = 0; i < _fileHeader.mapCount; ++i) {
+ _maps[i].rawDunDataOffset = dunDataStream.readUint16BE();
+ dunDataStream.readUint32BE(); // discard 4 bytes
+ _maps[i].offsetMapX = dunDataStream.readByte();
+ _maps[i].offsetMapY = dunDataStream.readByte();
+
+ uint16 tmp = dunDataStream.readUint16BE();
+ _maps[i].height = tmp >> 11;
+ _maps[i].width = (tmp >> 6) & 0x1F;
+ _maps[i].level = tmp & 0x1F; // Only used in DMII
+
+ tmp = dunDataStream.readUint16BE();
+ _maps[i].randFloorOrnCount = tmp >> 12;
+ _maps[i].floorOrnCount = (tmp >> 8) & 0xF;
+ _maps[i].randWallOrnCount = (tmp >> 4) & 0xF;
+ _maps[i].wallOrnCount = tmp & 0xF;
+
+ tmp = dunDataStream.readUint16BE();
+ _maps[i].difficulty = tmp >> 12;
+ _maps[i].creatureTypeCount = (tmp >> 4) & 0xF;
+ _maps[i].doorOrnCount = tmp & 0xF;
+
+ tmp = dunDataStream.readUint16BE();
+ _maps[i].doorSet1 = (tmp >> 12) & 0xF;
+ _maps[i].doorSet0 = (tmp >> 8) & 0xF;
+ _maps[i].wallSet = (tmp >> 4) & 0xF;
+ _maps[i].floorSet = tmp & 0xF;
+ }
+
+ // TODO: ??? is this - begin
+ if (_dunData.dunMapsFirstColumnIndex) delete[] _dunData.dunMapsFirstColumnIndex;
+
+ _dunData.dunMapsFirstColumnIndex = new uint16[_fileHeader.mapCount];
+ uint16 columCount = 0;
+ for (uint16 i = 0; i < _fileHeader.mapCount; ++i) {
+ _dunData.dunMapsFirstColumnIndex[i] = columCount;
+ columCount += _maps[i].width + 1;
+ }
+ _dunData.dunColumCount = columCount;
+ // TODO: ??? is this - end
+
+ if (_messages.newGame) // TODO: what purpose does this serve?
+ _fileHeader.squareFirstThingCount += 300;
+
+ // TODO: ??? is this - begin
+ if (_dunData.dunColumnsCumulativeSquareThingCount)
+ delete[] _dunData.dunColumnsCumulativeSquareThingCount;
+ _dunData.dunColumnsCumulativeSquareThingCount = new uint16[columCount];
+ for (uint16 i = 0; i < columCount; ++i)
+ _dunData.dunColumnsCumulativeSquareThingCount[i] = dunDataStream.readUint16BE();
+ // TODO: ??? is this - end
+
+ // TODO: ??? is this - begin
+ if (_dunData.squareFirstThings)
+ delete[] _dunData.squareFirstThings;
+ _dunData.squareFirstThings = new Thing[_fileHeader.squareFirstThingCount];
+ for (uint16 i = 0; i < _fileHeader.squareFirstThingCount; ++i) {
+ uint16 tmp = dunDataStream.readUint16BE();
+ _dunData.squareFirstThings[i].cell = tmp >> 14;
+ _dunData.squareFirstThings[i].type = (tmp >> 10) & 0xF;
+ _dunData.squareFirstThings[i].index = tmp & 0x1FF;
+ }
+ if (_messages.newGame)
+ for (uint16 i = 0; i < 300; ++i)
+ _dunData.squareFirstThings[i] = Thing::specThingNone;
+
+ // TODO: ??? is this - end
+
+ // load text data
+ if (_dunData.dunTextData)
+ delete[] _dunData.dunTextData;
+ _dunData.dunTextData = new uint16[_fileHeader.textDataWordCount];
+ for (uint16 i = 0; i < _fileHeader.textDataWordCount; ++i)
+ _dunData.dunTextData[i] = dunDataStream.readUint16BE();
+
+ // TODO: ??? what this
+ if (_messages.newGame)
+ _dunData.eventMaximumCount = 100;
+
+ // load 'Things'
+ // TODO: implement load things
+ // this is a temporary workaround to seek to raw map data
+ for (uint16 i = 0; i < kThingTypeTotal; ++i)
+ dunDataStream.skip(_fileHeader.thingCounts[i] * gThingDataByteCount[i]);
+
+ _rawMapData = _rawDunFileData + dunDataStream.pos();
+
+ if (_dunData.dungeonMapData) delete[] _dunData.dungeonMapData;
+
+ if (_messages.restartGameRequest) {
+ uint8 mapCount = _fileHeader.mapCount;
+ _dunData.dungeonMapData = new byte**[_dunData.dunColumCount + mapCount];
+ byte **colFirstSquares = _dunData.dungeonMapData[mapCount];
+ for (uint8 i = 0; i < mapCount; ++i) {
+ _dunData.dungeonMapData[i] = colFirstSquares;
+ byte *square = _rawMapData + _maps[i].rawDunDataOffset;
+ *colFirstSquares++ = square;
+ for (uint16 w = 0; w <= _maps[i].width; ++w) {
+ square += _maps[w].height + 1;
+ *colFirstSquares++ = square;
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/engines/dm/dungeonman.h b/engines/dm/dungeonman.h
index b820bb2b58..b26d56fc4c 100644
--- a/engines/dm/dungeonman.h
+++ b/engines/dm/dungeonman.h
@@ -5,16 +5,135 @@
namespace DM {
+class DungeonMan;
+
+enum ThingType {
+ kPartyThingType = -1, // @ CM1_THING_TYPE_PARTY, special value
+ kDoorThingType = 0,
+ kTeleporterThingType = 1,
+ kTextstringType = 2,
+ kSensorThingType = 3,
+ kGroupThingType = 4,
+ kWeaponThingType = 5,
+ kArmourThingType = 6,
+ kScrollThingType = 7,
+ kPotionThingType = 8,
+ kContainerThingType = 9,
+ kJunkThingType = 10,
+ kProjectileThingType = 14,
+ kExplosionThingType = 15,
+ kThingTypeTotal = 16 // +1 than the last
+}; // @ C[00..15]_THING_TYPE_...
+
+
+class DungeonFileHeader {
+ friend class DungeonMan;
+
+ uint16 dungeonId; // @ G0526_ui_DungeonID
+ // equal to dungeonId
+ uint16 ornamentRandomSeed;
+ uint32 rawMapDataSize;
+ uint8 mapCount;
+ uint16 textDataWordCount;
+ direction partyStartDir; // @ InitialPartyLocation
+ uint16 partyStartPosX, partyStartPosY;
+ uint16 squareFirstThingCount; // @ SquareFirstThingCount
+ uint16 thingCounts[16]; // @ ThingCount[16]
+}; // @ DUNGEON_HEADER
+
+class Thing {
+ friend class DungeonMan;
+
+ static const Thing specThingNone;
+
+ Thing(uint8 cell, uint8 type, uint8 index) : cell(cell), type(type), index(index) {}
+ Thing() {}
+
+ uint8 cell;
+ uint8 type;
+ uint8 index;
+}; // @ THING
+
+class DungeonData {
+ friend class DungeonMan;
+
+ direction partyDir; // @ G0308_i_PartyDirection
+ uint16 partyPosX; // @ G0306_i_PartyMapX
+ uint16 partyPosY; // @ G0307_i_PartyMapY
+ uint8 currMapIndex; // @ G0309_i_PartyMapIndex
+
+ // I have no idea the heck is this
+ uint16 *dunMapsFirstColumnIndex = NULL; // @ G0281_pui_DungeonMapsFirstColumnIndex
+ uint16 dunColumCount; // @ G0282_ui_DungeonColumnCount
+
+ // I have no idea the heck is this
+ uint16 *dunColumnsCumulativeSquareThingCount = NULL; // @ G0280_pui_DungeonColumnsCumulativeSquareThingCount
+ Thing *squareFirstThings = NULL; // @ G0283_pT_SquareFirstThings
+ uint16 *dunTextData = NULL; // @ G0260_pui_DungeonTextData
+
+ byte *rawThingData[16] = {NULL}; // @ G0284_apuc_ThingData
+
+ byte ***dungeonMapData = NULL; // @ G0279_pppuc_DungeonMapData
+
+ uint16 eventMaximumCount; // @ G0369_ui_EventMaximumCount
+}; // @ AGGREGATE
+
+struct Messages {
+ friend class DungeonMan;
+
+private:
+ bool newGame = true; // @ G0298_B_NewGame
+ bool restartGameRequest = false; // @ G0523_B_RestartGameRequested
+}; // @ AGGREGATE
+
+class Map {
+ friend class DungeonMan;
+
+ uint32 rawDunDataOffset;
+ uint8 offsetMapX, offsetMapY;
+
+ uint8 level; // only used in DMII
+ uint8 width, height; // THESRE ARE INCLUSIVE BOUNDARIES
+ // orn short for Ornament
+ uint8 wallOrnCount; /* May be used in a Sensor on a Wall or closed Fake Wall square */
+ uint8 randWallOrnCount; /* Used only on some Wall squares and some closed Fake Wall squares */
+ uint8 floorOrnCount; /* May be used in a Sensor on a Pit, open Fake Wall, Corridor or Teleporter square */
+ uint8 randFloorOrnCount; /* Used only on some Corridor squares and some open Fake Wall squares */
+
+ uint8 doorOrnCount;
+ uint8 creatureTypeCount;
+ uint8 difficulty;
+
+ uint8 floorSet, wallSet, doorSet0, doorSet1;
+
+}; // @ MAP
+
+
+
class DungeonMan {
DMEngine *_vm;
- uint32 _dungeonDataSize;
- byte *_dungeonData;
+
+ uint32 _rawDunFileDataSize;
+ byte *_rawDunFileData; // @ ???
+ DungeonFileHeader _fileHeader; // @ G0278_ps_DungeonHeader
+
+ DungeonData _dunData; // @ NONE
+ Map *_maps; // @ G0277_ps_DungeonMaps
+ // does not have to be freed
+ byte *_rawMapData; // @ G0276_puc_DungeonRawMapData
+ Messages _messages; // @ NONE
+
DungeonMan(const DungeonMan &other); // no implementation on purpose
void operator=(const DungeonMan &rhs); // no implementation on purpose
+
+ void mapCoordsAfterRelMovement(direction dir, uint16 stepsForward, uint16 stepsRight, uint16 &posX, uint16 &posY); // @ F0150_DUNGEON_UpdateMapCoordinatesAfterRelativeMovement
+
+ void decompressDungeonFile(); // @ F0455_FLOPPY_DecompressDungeon
public:
DungeonMan(DMEngine *dmEngine);
~DungeonMan();
- void loadDungeonFile();
+ // TODO: this does stuff other than load the file!
+ void loadDungeonFile(); // @ F0434_STARTEND_IsLoadDungeonSuccessful_CPSC
};
}
diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 0ae3b251fd..0c2efc6b71 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -39,7 +39,7 @@ Frame gCeilingFrame = {0, 223, 0, 28, 0, 0};
Frame gFloorFrame = {0, 223, 66, 135, 0, 0};
extern Viewport gDefultViewPort = {0, 0};
-extern Viewport gDungeonViewport = {0, 64};
+extern Viewport gDungeonViewport = {0, 64}; // TODO: I guessed the numbers
}