1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "map.h"
4
5#include <base/log.h>
6
7#include <engine/storage.h>
8
9#include <game/mapitems.h>
10
11CMap::CMap() = default;
12
13int CMap::GetDataSize(int Index) const
14{
15 return m_DataFile.GetDataSize(Index);
16}
17
18void *CMap::GetData(int Index)
19{
20 return m_DataFile.GetData(Index);
21}
22
23void *CMap::GetDataSwapped(int Index)
24{
25 return m_DataFile.GetDataSwapped(Index);
26}
27
28const char *CMap::GetDataString(int Index)
29{
30 return m_DataFile.GetDataString(Index);
31}
32
33void CMap::UnloadData(int Index)
34{
35 m_DataFile.UnloadData(Index);
36}
37
38int CMap::NumData() const
39{
40 return m_DataFile.NumData();
41}
42
43int CMap::GetItemSize(int Index)
44{
45 return m_DataFile.GetItemSize(Index);
46}
47
48void *CMap::GetItem(int Index, int *pType, int *pId)
49{
50 return m_DataFile.GetItem(Index, pType, pId);
51}
52
53void CMap::GetType(int Type, int *pStart, int *pNum)
54{
55 m_DataFile.GetType(Type, pStart, pNum);
56}
57
58int CMap::FindItemIndex(int Type, int Id)
59{
60 return m_DataFile.FindItemIndex(Type, Id);
61}
62
63void *CMap::FindItem(int Type, int Id)
64{
65 return m_DataFile.FindItem(Type, Id);
66}
67
68int CMap::NumItems() const
69{
70 return m_DataFile.NumItems();
71}
72
73bool CMap::Load(const char *pMapName)
74{
75 IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
76 if(!pStorage)
77 return false;
78
79 // Ensure current datafile is not left in an inconsistent state if loading fails,
80 // by loading the new datafile separately first.
81 CDataFileReader NewDataFile;
82 if(!NewDataFile.Open(pStorage, pFilename: pMapName, StorageType: IStorage::TYPE_ALL))
83 return false;
84
85 // Check version
86 const CMapItemVersion *pItem = (CMapItemVersion *)NewDataFile.FindItem(Type: MAPITEMTYPE_VERSION, Id: 0);
87 if(pItem == nullptr || pItem->m_Version != CMapItemVersion::CURRENT_VERSION)
88 {
89 log_error("map/load", "Error: map version not supported.");
90 NewDataFile.Close();
91 return false;
92 }
93
94 // Replace compressed tile layers with uncompressed ones
95 int GroupsStart, GroupsNum, LayersStart, LayersNum;
96 NewDataFile.GetType(Type: MAPITEMTYPE_GROUP, pStart: &GroupsStart, pNum: &GroupsNum);
97 NewDataFile.GetType(Type: MAPITEMTYPE_LAYER, pStart: &LayersStart, pNum: &LayersNum);
98 for(int g = 0; g < GroupsNum; g++)
99 {
100 const CMapItemGroup *pGroup = static_cast<CMapItemGroup *>(NewDataFile.GetItem(Index: GroupsStart + g));
101 for(int l = 0; l < pGroup->m_NumLayers; l++)
102 {
103 CMapItemLayer *pLayer = static_cast<CMapItemLayer *>(NewDataFile.GetItem(Index: LayersStart + pGroup->m_StartLayer + l));
104 if(pLayer->m_Type == LAYERTYPE_TILES)
105 {
106 CMapItemLayerTilemap *pTilemap = reinterpret_cast<CMapItemLayerTilemap *>(pLayer);
107 if(pTilemap->m_Version >= CMapItemLayerTilemap::TILE_SKIP_MIN_VERSION)
108 {
109 const size_t TilemapCount = (size_t)pTilemap->m_Width * pTilemap->m_Height;
110 const size_t TilemapSize = TilemapCount * sizeof(CTile);
111
112 if(((int)TilemapCount / pTilemap->m_Width != pTilemap->m_Height) || (TilemapSize / sizeof(CTile) != TilemapCount))
113 {
114 log_error("map/load", "map layer too big (%d * %d * %d causes an integer overflow)", pTilemap->m_Width, pTilemap->m_Height, sizeof(CTile));
115 return false;
116 }
117 CTile *pTiles = static_cast<CTile *>(malloc(size: TilemapSize));
118 if(!pTiles)
119 return false;
120 ExtractTiles(pDest: pTiles, DestSize: (size_t)pTilemap->m_Width * pTilemap->m_Height, pSrc: static_cast<CTile *>(NewDataFile.GetData(Index: pTilemap->m_Data)), SrcSize: NewDataFile.GetDataSize(Index: pTilemap->m_Data) / sizeof(CTile));
121 NewDataFile.ReplaceData(Index: pTilemap->m_Data, pData: reinterpret_cast<char *>(pTiles), Size: TilemapSize);
122 }
123 }
124 }
125 }
126
127 // Replace existing datafile with new datafile
128 m_DataFile.Close();
129 m_DataFile = std::move(NewDataFile);
130 return true;
131}
132
133void CMap::Unload()
134{
135 m_DataFile.Close();
136}
137
138bool CMap::IsLoaded() const
139{
140 return m_DataFile.IsOpen();
141}
142
143IOHANDLE CMap::File() const
144{
145 return m_DataFile.File();
146}
147
148SHA256_DIGEST CMap::Sha256() const
149{
150 return m_DataFile.Sha256();
151}
152
153unsigned CMap::Crc() const
154{
155 return m_DataFile.Crc();
156}
157
158int CMap::MapSize() const
159{
160 return m_DataFile.MapSize();
161}
162
163void CMap::ExtractTiles(CTile *pDest, size_t DestSize, const CTile *pSrc, size_t SrcSize)
164{
165 size_t DestIndex = 0;
166 size_t SrcIndex = 0;
167 while(DestIndex < DestSize && SrcIndex < SrcSize)
168 {
169 for(unsigned Counter = 0; Counter <= pSrc[SrcIndex].m_Skip && DestIndex < DestSize; Counter++)
170 {
171 pDest[DestIndex] = pSrc[SrcIndex];
172 pDest[DestIndex].m_Skip = 0;
173 DestIndex++;
174 }
175 SrcIndex++;
176 }
177}
178
179extern IEngineMap *CreateEngineMap() { return new CMap; }
180