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