1 /*
2 * Copyright 2010-2014 OpenXcom Developers.
3 *
4 * This file is part of OpenXcom.
5 *
6 * OpenXcom is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OpenXcom is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "MapDataSet.h"
20 #include "MapData.h"
21 #include <fstream>
22 #include <sstream>
23 #include <SDL_endian.h>
24 #include "../Engine/Exception.h"
25 #include "../Engine/SurfaceSet.h"
26 #include "../Engine/CrossPlatform.h"
27
28 namespace OpenXcom
29 {
30
31 MapData *MapDataSet::_blankTile = 0;
32 MapData *MapDataSet::_scorchedTile = 0;
33
34 /**
35 * MapDataSet construction.
36 */
MapDataSet(const std::string & name)37 MapDataSet::MapDataSet(const std::string &name) : _name(name), _objects(), _surfaceSet(0), _loaded(false)
38 {
39 }
40
41 /**
42 * MapDataSet destruction.
43 */
~MapDataSet()44 MapDataSet::~MapDataSet()
45 {
46 unloadData();
47 }
48
49 /**
50 * Loads the map data set from a YAML file.
51 * @param node YAML node.
52 */
load(const YAML::Node & node)53 void MapDataSet::load(const YAML::Node &node)
54 {
55 for (YAML::const_iterator i = node.begin(); i != node.end(); ++i)
56 {
57 _name = i->as<std::string>(_name);
58 }
59 }
60
61 /**
62 * Gets the MapDataSet name (string).
63 * @return The MapDataSet name.
64 */
getName() const65 std::string MapDataSet::getName() const
66 {
67 return _name;
68 }
69
70 /**
71 * Gets the MapDataSet size.
72 * @return The size in number of records.
73 */
getSize() const74 size_t MapDataSet::getSize() const
75 {
76 return _objects.size();
77 }
78
79 /**
80 * Gets the objects in this dataset.
81 * @return Pointer to the objects.
82 */
getObjects()83 std::vector<MapData*> *MapDataSet::getObjects()
84 {
85 return &_objects;
86 }
87
88 /**
89 * Gets the surfaces in this dataset.
90 * @return Pointer to the surfaceset.
91 */
getSurfaceset() const92 SurfaceSet *MapDataSet::getSurfaceset() const
93 {
94 return _surfaceSet;
95 }
96
97 /**
98 * Loads terrain data in XCom format (MCD & PCK files).
99 * @sa http://www.ufopaedia.org/index.php?title=MCD
100 */
loadData()101 void MapDataSet::loadData()
102 {
103 // prevents loading twice
104 if (_loaded) return;
105 _loaded = true;
106
107 int objNumber = 0;
108
109 // the struct below helps to read the xcom file format
110 #pragma pack(push, 1)
111 struct MCD
112 {
113 unsigned char Frame[8];
114 unsigned char LOFT[12];
115 unsigned short ScanG;
116 unsigned char u23;
117 unsigned char u24;
118 unsigned char u25;
119 unsigned char u26;
120 unsigned char u27;
121 unsigned char u28;
122 unsigned char u29;
123 unsigned char u30;
124 unsigned char UFO_Door;
125 unsigned char Stop_LOS;
126 unsigned char No_Floor;
127 unsigned char Big_Wall;
128 unsigned char Gravlift;
129 unsigned char Door;
130 unsigned char Block_Fire;
131 unsigned char Block_Smoke;
132 unsigned char u39;
133 unsigned char TU_Walk;
134 unsigned char TU_Slide;
135 unsigned char TU_Fly;
136 unsigned char Armor;
137 unsigned char HE_Block;
138 unsigned char Die_MCD;
139 unsigned char Flammable;
140 unsigned char Alt_MCD;
141 unsigned char u48;
142 signed char T_Level;
143 unsigned char P_Level;
144 unsigned char u51;
145 unsigned char Light_Block;
146 unsigned char Footstep;
147 unsigned char Tile_Type;
148 unsigned char HE_Type;
149 unsigned char HE_Strength;
150 unsigned char Smoke_Blockage;
151 unsigned char Fuel;
152 unsigned char Light_Source;
153 unsigned char Target_Type;
154 unsigned char Xcom_Base;
155 unsigned char u62;
156 };
157 #pragma pack(pop)
158
159 MCD mcd;
160
161 // Load Terrain Data from MCD file
162 std::ostringstream s;
163 s << "TERRAIN/" << _name << ".MCD";
164
165 // Load file
166 std::ifstream mapFile (CrossPlatform::getDataFile(s.str()).c_str(), std::ios::in | std::ios::binary);
167 if (!mapFile)
168 {
169 throw Exception(s.str() + " not found");
170 }
171
172 while (mapFile.read((char*)&mcd, sizeof(MCD)))
173 {
174 MapData *to = new MapData(this);
175 _objects.push_back(to);
176
177 // set all the terrainobject properties:
178 for (int frame = 0; frame < 8; frame++)
179 {
180 to->setSprite(frame,(int)mcd.Frame[frame]);
181 }
182 to->setYOffset((int)mcd.P_Level);
183 to->setSpecialType((int)mcd.Target_Type, (int)mcd.Tile_Type);
184 to->setTUCosts((int)mcd.TU_Walk, (int)mcd.TU_Fly, (int)mcd.TU_Slide);
185 to->setFlags(mcd.UFO_Door != 0, mcd.Stop_LOS != 0, mcd.No_Floor != 0, (int)mcd.Big_Wall, mcd.Gravlift != 0, mcd.Door != 0, mcd.Block_Fire != 0, mcd.Block_Smoke != 0, mcd.Xcom_Base != 0);
186 to->setTerrainLevel((int)mcd.T_Level);
187 to->setFootstepSound((int)mcd.Footstep);
188 to->setAltMCD((int)(mcd.Alt_MCD));
189 to->setDieMCD((int)(mcd.Die_MCD));
190 to->setBlockValue((int)mcd.Light_Block, (int)mcd.Stop_LOS, (int)mcd.HE_Block, (int)mcd.Block_Smoke, (int)mcd.Flammable, (int)mcd.HE_Block);
191 to->setLightSource((int)mcd.Light_Source);
192 to->setArmor((int)mcd.Armor);
193 to->setFlammable((int)mcd.Flammable);
194 to->setFuel((int)mcd.Fuel);
195 to->setExplosive((int)mcd.HE_Strength);
196 mcd.ScanG = SDL_SwapLE16(mcd.ScanG);
197 to->setMiniMapIndex(mcd.ScanG);
198
199 for (int layer = 0; layer < 12; layer++)
200 {
201 int loft = (int)mcd.LOFT[layer];
202 to->setLoftID(loft, layer);
203 }
204
205 // store the 2 tiles of blanks in a static - so they are accessible everywhere
206 if (_name.compare("BLANKS") == 0)
207 {
208 if (objNumber == 0)
209 MapDataSet::_blankTile = to;
210 else if (objNumber == 1)
211 MapDataSet::_scorchedTile = to;
212 }
213 objNumber++;
214 }
215
216
217 if (!mapFile.eof())
218 {
219 throw Exception("Invalid MCD file");
220 }
221
222 mapFile.close();
223
224 // process the mapdataset to put block values on floortiles (as we don't have em in UFO)
225 for (std::vector<MapData*>::iterator i = _objects.begin(); i != _objects.end(); ++i)
226 {
227 if ((*i)->getObjectType() == MapData::O_FLOOR && (*i)->getBlock(DT_HE) == 0)
228 {
229 (*i)->setBlockValue(1,1,(*i)->getArmor(),1,1,(*i)->getArmor());
230 if ((*i)->getDieMCD())
231 {
232 _objects.at((*i)->getDieMCD())->setBlockValue(1,1,(*i)->getArmor(),1,1,(*i)->getArmor());
233 }
234 }
235 }
236
237 // Load terrain sprites/surfaces/PCK files into a surfaceset
238 std::ostringstream s1,s2;
239 s1 << "TERRAIN/" << _name << ".PCK";
240 s2 << "TERRAIN/" << _name << ".TAB";
241 _surfaceSet = new SurfaceSet(32, 40);
242 _surfaceSet->loadPck(CrossPlatform::getDataFile(s1.str()), CrossPlatform::getDataFile(s2.str()));
243
244 }
245
246 /**
247 * Unloads the terrain data.
248 */
unloadData()249 void MapDataSet::unloadData()
250 {
251 if (_loaded)
252 {
253 for (std::vector<MapData*>::iterator i = _objects.begin(); i != _objects.end();)
254 {
255 delete *i;
256 i = _objects.erase(i);
257 }
258 delete _surfaceSet;
259 _loaded = false;
260 }
261 }
262
263 /**
264 * Loads the LOFTEMPS.DAT into the ruleset voxeldata.
265 * @param filename Filename of the DAT file.
266 * @param voxelData The ruleset.
267 */
loadLOFTEMPS(const std::string & filename,std::vector<Uint16> * voxelData)268 void MapDataSet::loadLOFTEMPS(const std::string &filename, std::vector<Uint16> *voxelData)
269 {
270 // Load file
271 std::ifstream mapFile (filename.c_str(), std::ios::in | std::ios::binary);
272 if (!mapFile)
273 {
274 throw Exception(filename + " not found");
275 }
276
277 Uint16 value;
278
279 while (mapFile.read((char*)&value, sizeof(value)))
280 {
281 value = SDL_SwapLE16(value);
282 voxelData->push_back(value);
283 }
284
285 if (!mapFile.eof())
286 {
287 throw Exception("Invalid LOFTEMPS");
288 }
289
290 mapFile.close();
291 }
292
293 /**
294 * Gets a blank floor tile.
295 * @return Pointer to a blank tile.
296 */
getBlankFloorTile()297 MapData *MapDataSet::getBlankFloorTile()
298 {
299 return MapDataSet::_blankTile;
300 }
301
302 /**
303 * Gets a scorched earth tile.
304 * @return Pointer to a scorched earth tile.
305 */
getScorchedEarthTile()306 MapData *MapDataSet::getScorchedEarthTile()
307 {
308 return MapDataSet::_scorchedTile;
309 }
310
311 }
312